Module: Lich::Gemstone::Combat::Observers
- Defined in:
- documented/gemstone/combat/observers.rb
Overview
Subscription feed for parsed combat events: damage, wounds, status changes, and creature state transitions.
The Creature registry is the read model for current state; Observers emit the transient events that state queries cannot provide: edges (transitions like stunned→unstunned), per-event ledger details (individual damage instances), and session-only facts before they are swept.
Subscribers must be cheap, non-blocking, and may never send game commands (fput, Spell#cast, PSMS.use) - queue work for your own script thread. Errors are isolated, logged, and never break other subscribers.
Class Method Summary collapse
-
.any_for?(type) ⇒ Boolean
Returns true if there are any subscribers for the given event type or :any.
-
.clear! ⇒ void
Removes all subscribers and named registrations.
-
.emit(type, data) ⇒ Object
Emit an event to type + :any subscribers.
-
.off(handler_or_name) ⇒ Object
Remove a handler - pass the Proc returned by Observers.on, or the name it was registered under.
-
.on(*types, name: nil, &block) ⇒ Object
Subscribe to one or more event types (or :any for everything).
Class Method Details
.any_for?(type) ⇒ Boolean
Returns true if there are any subscribers for the given event type or :any.
Allows the processor to avoid constructing event payloads when nobody is listening.
137 138 139 |
# File 'documented/gemstone/combat/observers.rb', line 137 def any_for?(type) @mutex.synchronize { !@subscribers[type].empty? || !@subscribers[:any].empty? } end |
.clear! ⇒ void
This method returns an undefined value.
Removes all subscribers and named registrations.
Used during processor teardown and testing.
146 147 148 149 150 151 |
# File 'documented/gemstone/combat/observers.rb', line 146 def clear! @mutex.synchronize do @subscribers.clear @named.clear end end |
.emit(type, data) ⇒ Object
Emit an event to type + :any subscribers. Subscriber errors are isolated and logged, never raised to the caller (the processor).
117 118 119 120 121 122 123 124 125 126 127 |
# File 'documented/gemstone/combat/observers.rb', line 117 def emit(type, data) handlers = @mutex.synchronize { @subscribers[type].dup + @subscribers[:any].dup } handlers.each do |handler| begin handler.call(type, data) rescue StandardError => e Lich.log "error: Combat::Observers subscriber (#{type}): #{e.}\n\t#{e.backtrace&.first}" end end nil end |
.off(handler_or_name) ⇒ Object
Remove a handler - pass the Proc returned by on, or the name it was registered under.
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'documented/gemstone/combat/observers.rb', line 102 def off(handler_or_name) @mutex.synchronize do handler = if handler_or_name.is_a?(Proc) handler_or_name else @named.delete(handler_or_name.to_s) end @named.delete_if { |_, h| h == handler } @subscribers.each_value { |list| list.delete(handler) } if handler end nil end |
.on(*types, name: nil, &block) ⇒ Object
Subscribe to one or more event types (or :any for everything). Returns the block; keep it to unsubscribe via .off.
With name:, registration is idempotent (DownstreamHook.add semantics): re-registering the same name replaces the previous handler instead of stacking - safe for script restarts and interactive ;e testing.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'documented/gemstone/combat/observers.rb', line 85 def on(*types, name: nil, &block) raise ArgumentError, 'block required' unless block types = [:any] if types.empty? @mutex.synchronize do if name old = @named.delete(name.to_s) @subscribers.each_value { |list| list.delete(old) } if old @named[name.to_s] = block end types.each { |t| @subscribers[t.to_sym] << block } end block end |