New SnapshotEventRouter class (46 lines) provides callable-based snapshot dispatch via register(), register_always(), and dispatch(). main.gd _process() now calls _router.dispatch(snapshot) instead of 15+ inline if-has blocks. Handlers registered in _ready(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
46 lines
1.9 KiB
GDScript
46 lines
1.9 KiB
GDScript
class_name SnapshotEventRouter
|
|
## Routes snapshot fields to registered handlers (D-020, #559).
|
|
##
|
|
## Decouples main.gd from knowing which child node handles which snapshot field.
|
|
## Handlers are registered in main.gd._ready(); dispatch() is called each snapshot tick.
|
|
##
|
|
## Two handler types:
|
|
## - Keyed: called only when the snapshot contains a specific field.
|
|
## - Always: called every dispatch (every snapshot tick), regardless of fields present.
|
|
##
|
|
## All handlers are zero-argument callables — they read from GameState directly.
|
|
## This preserves GameState as the single source of truth post-apply_snapshot().
|
|
|
|
## Keyed handlers: field_name → Array[Callable]
|
|
## Array per field allows multiple handlers on the same key (e.g., two consumers of same data).
|
|
var _keyed: Dictionary = {} # String → Array[Callable]
|
|
|
|
## Always handlers: called every dispatch regardless of snapshot content.
|
|
var _always: Array[Callable] = []
|
|
|
|
|
|
## Register a handler for a specific snapshot field key.
|
|
## Handler is called (with no arguments) when snapshot.has(field) is true.
|
|
## Multiple handlers per field are supported — they run in registration order.
|
|
func register(field: String, handler: Callable) -> void:
|
|
if not _keyed.has(field):
|
|
_keyed[field] = []
|
|
_keyed[field].append(handler)
|
|
|
|
|
|
## Register a handler that runs every dispatch tick (not keyed to a field).
|
|
## Use for child nodes that update from GameState on every snapshot, e.g. update_from_state().
|
|
func register_always(handler: Callable) -> void:
|
|
_always.append(handler)
|
|
|
|
|
|
## Dispatch a snapshot: call always handlers first, then keyed handlers for present fields.
|
|
## Handlers read from GameState.* directly — apply_snapshot() must be called before dispatch().
|
|
func dispatch(snapshot: Dictionary) -> void:
|
|
for handler in _always:
|
|
handler.call()
|
|
for field in _keyed:
|
|
if snapshot.has(field):
|
|
for handler in _keyed[field]:
|
|
handler.call()
|