Module: StoryTeller::Daemons
- Included in:
- Inform::Object
- Defined in:
- lib/story_teller/daemon.rb
Overview
The StoryTeller::Daemons module
Defined Under Namespace
Classes: Entheogen
Constant Summary collapse
- DaemonScanElapsedMessage =
"Scanned %<daemons>s daemons in %<elapsed>0.2f milliseconds".freeze
- DaemonExecutionElapsedMessage =
"\e[1m\e[33mExecuted daemons in %<elapsed>0.2f milliseconds\e[39m\e[22m".freeze
- RecordNotFoundPattern =
%r{Record not found}.freeze
Class Method Summary collapse
- .daemons ⇒ Object
- .ensure_inform_library(obj) ⇒ Object
- .execute(obj) ⇒ Object
-
.execute_daemon(obj) ⇒ Object
TODO: Potentially go ahead and print any string results from the daemon invocation.
- .execute_each_turn(obj) ⇒ Object
- .refresh_or_remove(obj) ⇒ Object
- .scan_objects ⇒ Object
-
.start ⇒ Object
rubocop: disable Metrics/AbcSize.
-
.stop ⇒ Object
rubocop: enable Metrics/AbcSize.
Instance Method Summary collapse
- #before_destroy ⇒ Object
- #daemonic? ⇒ Boolean
- #daemonize(event) ⇒ Object
- #ghost ⇒ Object
- #heart ⇒ Object
- #init_heart ⇒ Object
-
#occasionally(time = nil, &block) ⇒ Object
rubocop: disable Metrics/AbcSize.
- #on? ⇒ Boolean
- #rescan ⇒ Object
- #schedules ⇒ Object
- #spawn ⇒ Object
- #StartDaemon(obj) ⇒ Object
- #StartTimer(obj, interval) ⇒ Object
- #StopDaemon(obj) ⇒ Object
- #StopTimer(obj) ⇒ Object
Class Method Details
.daemons ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/story_teller/daemon.rb', line 160 def self.daemons start = Time.ms StoryTeller::Daemons.spawn.each do |daemon| # In realtime games, each_turn is a synonym for daemon next unless daemon.respond_to?(:daemon) || daemon.respond_to?(:each_turn) StoryTeller::Daemons.execute daemon end ensure finish = Time.ms elapsed = finish - start log.debug format(DaemonExecutionElapsedMessage, elapsed: elapsed) if finish % 300_000 == 0 end |
.ensure_inform_library(obj) ⇒ Object
182 183 184 185 186 187 188 |
# File 'lib/story_teller/daemon.rb', line 182 def self.ensure_inform_library(obj) return unless obj.inflib.nil? # TODO: Use a pool of InformLibraries until # comprehensive event contexts can be implemented log.warn "Using singleton Ghost InformLibrary for #{obj} daemon" obj.inflib = StoryTeller::Engine.libraries[StoryTeller::Daemons.ghost] end |
.execute(obj) ⇒ Object
206 207 208 209 210 211 212 213 214 215 |
# File 'lib/story_teller/daemon.rb', line 206 def self.execute(obj) return if obj.nil? if obj.respond_to? :daemon execute_daemon(obj) elsif obj.respond_to? :each_turn execute_each_turn(obj) end rescue StandardError => e log.error "Error executing daemon for #{obj}", e end |
.execute_daemon(obj) ⇒ Object
TODO: Potentially go ahead and print any string results from the daemon invocation. For example:
obj.println obj.daemon
194 195 196 197 198 |
# File 'lib/story_teller/daemon.rb', line 194 def self.execute_daemon(obj) refresh_or_remove(obj) ensure_inform_library(obj) obj.daemon end |
.execute_each_turn(obj) ⇒ Object
200 201 202 203 204 |
# File 'lib/story_teller/daemon.rb', line 200 def self.execute_each_turn(obj) refresh_or_remove(obj) ensure_inform_library(obj) obj.each_turn end |
.refresh_or_remove(obj) ⇒ Object
175 176 177 178 179 180 |
# File 'lib/story_teller/daemon.rb', line 175 def self.refresh_or_remove(obj) return if obj.nil? obj.refresh rescue StandardError => e Spawn.remove obj if RecordNotFoundPattern.match?(e.) end |
.scan_objects ⇒ Object
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/story_teller/daemon.rb', line 127 def self.scan_objects start = Time.now Inform::Object.all.each do |obj| Spawn.add obj if obj.respond_to?(:daemon) || obj.respond_to?(:each_turn) end rescue StandardError => e log.error "Error executing daemon method: #{e.}", e ensure log.debug format(DaemonScanElapsedMessage, daemons: Spawn.length, elapsed: Time.now - start) end |
.start ⇒ Object
rubocop: disable Metrics/AbcSize
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/story_teller/daemon.rb', line 139 def self.start initial_delay = StoryTeller::Game.config[:daemon_initial_delay] period = StoryTeller::Game.config[:daemons_period] unit = defined?(Java) ? java.util.concurrent.TimeUnit::MILLISECONDS : 'milliseconds' Thread.new do StoryTeller::Daemons.rescan log.debug "Starting daemons..." daemons = StoryTeller::Daemons.method(:daemons).to_proc StoryTeller::Daemons.ghost.heartbeat = StoryTeller::Daemons.heart.scheduleAtFixedRate( daemons, initial_delay, period, unit) end end |
.stop ⇒ Object
rubocop: enable Metrics/AbcSize
153 154 155 156 |
# File 'lib/story_teller/daemon.rb', line 153 def self.stop StoryTeller::Daemons.ghost.heartbeat.cancel(false) StoryTeller::Daemons.spawn.clear end |
Instance Method Details
#before_destroy ⇒ Object
244 245 246 247 248 |
# File 'lib/story_teller/daemon.rb', line 244 def before_destroy log.debug "#{self}: before_destroy stopping daemon" StopDaemon(self) if daemonic? super end |
#daemonic? ⇒ Boolean
240 241 242 |
# File 'lib/story_teller/daemon.rb', line 240 def daemonic? StoryTeller::Daemons.schedules.include? self end |
#daemonize(event) ⇒ Object
236 237 238 |
# File 'lib/story_teller/daemon.rb', line 236 def daemonize(event) StoryTeller::Daemons.schedules[self] = event end |
#ghost ⇒ Object
107 108 109 |
# File 'lib/story_teller/daemon.rb', line 107 def ghost @ghost ||= Entheogen.new end |
#heart ⇒ Object
90 91 92 |
# File 'lib/story_teller/daemon.rb', line 90 def heart @heart ||= init_heart end |
#init_heart ⇒ Object
94 95 96 97 98 |
# File 'lib/story_teller/daemon.rb', line 94 def init_heart # TODO: Figure out ruby-concurrency Executors? return unless defined?(Java) java.util.concurrent.Executors.newSingleThreadScheduledExecutor() end |
#occasionally(time = nil, &block) ⇒ Object
rubocop: disable Metrics/AbcSize
251 252 253 254 255 256 257 258 259 260 |
# File 'lib/story_teller/daemon.rb', line 251 def occasionally(time = nil, &block) time = StoryTeller::Game.config[:daemons_frequency_default] if time.nil? return if active? || daemonic? frequency = ((self.&:frequency) || time).to_i occasion = rand(frequency) + 1 log.trace "Scheduling occasional daemonic event for #{self} in #{occasion} seconds" (StoryTeller::Daemons.schedules[self] = delay(occasion, &block)).finally do StoryTeller::Daemons.schedules.delete self end end |
#on? ⇒ Boolean
115 116 117 118 |
# File 'lib/story_teller/daemon.rb', line 115 def on? return false if StoryTeller::Daemons.ghost.heartbeat.nil? StoryTeller::Daemons.ghost.heartbeat.isCancelled() == false end |
#rescan ⇒ Object
120 121 122 123 |
# File 'lib/story_teller/daemon.rb', line 120 def rescan StoryTeller::Daemons.spawn.clear StoryTeller::Daemons.scan_objects end |
#schedules ⇒ Object
111 112 113 |
# File 'lib/story_teller/daemon.rb', line 111 def schedules @schedules ||= defined?(Java) ? java.util.concurrent.ConcurrentHashMap.new : {} end |
#spawn ⇒ Object
86 87 88 |
# File 'lib/story_teller/daemon.rb', line 86 def spawn @spawn ||= defined?(Java) ? java.util.concurrent.ConcurrentSkipListSet.new : Set.new end |
#StartDaemon(obj) ⇒ Object
217 218 219 |
# File 'lib/story_teller/daemon.rb', line 217 def StartDaemon(obj) StoryTeller::Daemons.spawn.add obj if obj.respond_to?(:daemon) || obj.respond_to?(:each_turn) end |
#StartTimer(obj, interval) ⇒ Object
228 229 230 |
# File 'lib/story_teller/daemon.rb', line 228 def StartTimer(obj, interval) # TODO: Implement end |
#StopDaemon(obj) ⇒ Object
221 222 223 224 225 226 |
# File 'lib/story_teller/daemon.rb', line 221 def StopDaemon(obj) return if obj.nil? log.warn "Stopping daemon: #{obj}" obj.undef_method(:daemon) # TODO: Maybe don't do this StoryTeller::Daemons.spawn.remove obj end |
#StopTimer(obj) ⇒ Object
232 233 234 |
# File 'lib/story_teller/daemon.rb', line 232 def StopTimer(obj) # TODO: Implement end |