Closed rrbox closed 4 months ago
class EventWriter<T> {
}
class EventReader<T> {
}
💡Combine の pass through subject を使うことができるかもしれないです
class EventCommand {
func runEventResponder(in world: World) {
}
}
class EventSystemExecute<T>: SystemExecute {
func receive(event: EventReader<T>, worldBuffer: WorldBuffer) {
}
}
class EventSystem<T, Parameter: SystemParameter>: EventSystemExecute<T> {
}
extension World {
@discardableResult func addEventSystem<T, Parameter: SystemParameter>(_ system: EventSystem<T, Parameter>) -> World {
self.worldBuffer.systemBuffer.addSystem(system, as: EventSystemExecute<T>.self)
return self
}
func receive<T>(event: EventReader<T>) {
for system in self.worldBuffer.systemBuffer.systems(ofType: EventSystemExecute<T>.self) {
system.receive(event: event, worldBuffer: self.worldBuffer)
}
}
}
public struct EventReader<T> {
public let value: T
}
class Event<T>: EventCommand {
let value: T
init(value: T) {
self.value = value
}
override func runEventResponder(in world: World) {
world.receive(event: EventReader(value: self.value))
}
}
// world buffer に追加しておく (world + Init)
class EventQueue: BufferElement {
var eventCommandQueue = [EventCommand]()
}
// world buffer にプロパティをつけておく
class EventQueueBuffer {
let buffer: Buffer
init(buffer: Buffer) {
self.buffer = buffer
}
func eventQueue() -> EventQueue {
self.buffer.component(ofType: EventQueue.self)!
}
}
// Commands と基本的な仕組みは同じ.
final public class EventWriter<T>: SystemParameter {
let eventQueue: EventQueue
init(eventQueue: EventQueue) {
self.eventQueue = eventQueue
}
public func push(_ event: T) {
self.eventQueue.eventCommandQueue.append(Event<T>(value: event))
}
public static func register(to worldBuffer: WorldBuffer) {
}
public static func getParameter(from worldBuffer: WorldBuffer) -> EventWriter<T>? {
EventWriter<T>(eventQueue: EventQueueBuffer(buffer: worldBuffer).eventQueue())
}
}
ここまで作ってみましたが、World に buffer 内に System の保管領域を確保する際につまづいてしまいました。他の方法を検討してみたいです。
Combine を使用した方法も試してみました。
import Combine
import Foundation
public class EventExecute<T>: SystemExecute {
var cancellable: AnyCancellable?
func receiveEvent(_ event: T, worldBuffer: WorldBuffer) {
}
func setSubject(from buffer: WorldBuffer) {
let subject = buffer.eventSubjectBuffer.getSubject(eventValueOfType: T.self)
self.cancellable = subject!.sink { [weak self, weak buffer] event in
self!.receiveEvent(event, worldBuffer: buffer!)
}
}
}
public struct EventReader<T> {
public let value: T
}
final public class EventSystem<T, Parameter: SystemParameter>: EventExecute<T> {
let execute: (EventReader<T>, Parameter) -> ()
public init(execute: @escaping (EventReader<T>, Parameter) -> ()) {
self.execute = execute
}
override func receiveEvent(_ event: T, worldBuffer: WorldBuffer) {
self.execute(EventReader(value: event), Parameter.getParameter(from: worldBuffer)!)
}
}
class EventSubjectBuffer {
class EventSubjectRegistry<T>: BufferElement {
let subject: PassthroughSubject<T, Never>
init(subject: PassthroughSubject<T, Never>) {
self.subject = subject
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let buffer: Buffer
init(buffer: Buffer) {
self.buffer = buffer
}
func registerSubject<T>(eventValueOfType type: T.Type) {
self.buffer.addComponent(EventSubjectRegistry<T>(subject: PassthroughSubject<T, Never>()))
}
func getSubject<T>(eventValueOfType type: T.Type) -> PassthroughSubject<T, Never>? {
self.buffer.component(ofType: EventSubjectRegistry<T>.self)?.subject
}
}
extension WorldBuffer {
var eventSubjectBuffer: EventSubjectBuffer {
EventSubjectBuffer(buffer: self)
}
}
final public class EventWriter<T>: SystemParameter {
let subject: PassthroughSubject<T, Never>
init(subject: PassthroughSubject<T, Never>) {
self.subject = subject
}
public func send(value: T) {
self.subject.send(value)
}
public static func register(to worldBuffer: WorldBuffer) {
}
public static func getParameter(from worldBuffer: WorldBuffer) -> EventWriter<T>? {
EventWriter<T>(subject: worldBuffer.eventSubjectBuffer.getSubject(eventValueOfType: T.self)!)
}
}
public extension World {
@discardableResult func addEventStreamer<T>(eventType: T.Type) -> World {
self.worldBuffer.systemBuffer.registerSystemRegistry(ofType: EventExecute<T>.self)
self.worldBuffer.eventSubjectBuffer.registerSubject(eventValueOfType: T.self)
return self
}
}
public extension World {
@discardableResult func addEventSystem<T, Parameter: SystemParameter>(_ system: EventSystem<T, Parameter>) -> World {
system.setSubject(from: self.worldBuffer)
self.worldBuffer.systemBuffer.addSystem(system, as: EventExecute<T>.self)
Parameter.register(to: self.worldBuffer)
return self
}
@discardableResult func addEventSystem<T, Parameter: SystemParameter>(_ execute: @escaping (EventReader<T>, Parameter) -> ()) -> World {
self.addEventSystem(EventSystem(execute: execute))
}
}
この実装は比較的うまく機能しますが、問題点もあります。それは、Combine が非同期的に実行されることによる、データ競合です。主に Commands の commandQueue で競合が起こるようです。Commands の仕様を非同期処理に強い設計にすればなんとかなりそうな気がします。
また、 API をある程度妥協して設計しました。同じようにすれば、一つ上のコメントの実装でもできるかもしれません。
Event system 内で event writer を操作できない不具合があるようです。 理由は、そもそも Event system 実行中に Event を追加しても、Event system 実行終了後に全て削除されてしまうためです。
解決策は以下の通りです。
処理中 event buffer を作成する方法を採用してみます。
0.1 で完全に実装しました。
event 発生後は world に一旦保管し、event builder でセットされたシステムが実行される際に消費されるようにしました。