Module: StoryTeller::Game::Loader

Included in:
Runtime
Defined in:
lib/story_teller/game/loader.rb

Overview

The Game::Loader module loads a game from a given directory.

Constant Summary collapse

PreGameComponents =
%i[modules].freeze
EnsureFirst =
[/emotes.inf/, /grammar.inf/].freeze
GrammarParsedInfoMessage =
'Parsed grammar %<file>s in %0.2<elapsed>f milliseconds'.freeze

Instance Method Summary collapse

Instance Method Details

#first_orphan_object_with_description_that_has_lightObject



106
107
108
# File 'lib/story_teller/game/loader.rb', line 106

def first_orphan_object_with_description_that_has_light
  Inform::Object.all.find { |o| !o.description.nil? && o.has?(:light) }&.name
end

#game_files(game_path) ⇒ Object



185
186
187
188
189
# File 'lib/story_teller/game/loader.rb', line 185

def game_files(game_path)
  return [game_path] if File.file?(game_path)

  Dir.glob(File.join(game_path, '*.rb')).select { |file| File.file?(file) }
end

#game_serialObject



132
133
134
# File 'lib/story_teller/game/loader.rb', line 132

def game_serial
  defined?(::Serial) ? ::Serial : File.mtime(@game_dir_path).strftime('%y%m%d')
end

#grammar_filesObject



238
239
240
241
242
243
244
# File 'lib/story_teller/game/loader.rb', line 238

def grammar_files
  files = Dir.glob(File.join(grammar_module_path, '*'))
  EnsureFirst.each do |pattern|
    prioritize_if(files) { |file_path| file_path.match?(pattern) }
  end
  files
end

#handle_load_failure(e) ⇒ Object



218
219
220
221
222
223
224
225
226
227
# File 'lib/story_teller/game/loader.rb', line 218

def handle_load_failure(e)
  case e.message
  when /PG::UndefinedTable/
    log.error "Fatal: Database initialization is required"
    abort
  else
    log.error "Error loading game: #{e.class.name}: #{e.message}"
    e.backtrace.each { |t| log.error t }
  end
end

#load_game(game_dir_path = game_path) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/story_teller/game/loader.rb', line 83

def load_game(game_dir_path = game_path)
  self.load_path = game_dir_path
  load_game_subcomponents(pre_game_components)
  load_game_files(top_level_game_path(game_dir_path))
  load_library
  load_game_subcomponents(post_game_components)
  load_grammars
  reset_constants
rescue StandardError => e
  handle_load_failure(e)
  raise
end

#load_game_files(game_path = @game_dir_path) ⇒ Object

def load_game_files(game_path = @game_dir_path)

log.info "Loading story game #{game.name} from #{game_path}"
Dir.glob(File.join(game_path, '*.rb')).each do |file|
  next unless File.file?(file)
  log.trace "Loading file: #{file}"
  require file
end
log.debug "Loaded story game #{game.name}"

end



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/story_teller/game/loader.rb', line 162

def load_game_files(game_path = @game_dir_path)
  log.info "Loading story game #{game.name} from #{game_path}"

  if preserve_persisted_world?
    load_game_files_without_committing(game_path)
  else
    load_game_files_with_committing(game_path)
  end

  log.debug "Loaded story game #{game.name}"
end

#load_game_files_with_committing(game_path) ⇒ Object



178
179
180
181
182
183
# File 'lib/story_teller/game/loader.rb', line 178

def load_game_files_with_committing(game_path)
  game_files(game_path).each do |file|
    log.trace "Loading file: #{file}"
    require file
  end
end

#load_game_files_without_committing(game_path) ⇒ Object



191
192
193
194
195
196
197
198
199
# File 'lib/story_teller/game/loader.rb', line 191

def load_game_files_without_committing(game_path)
  preserved_ids = persistence.world_object_ids

  persistence.without_committing! do
    load_game_files_with_committing(game_path)
  end

  refresh_preserved_world_objects(preserved_ids)
end

#load_game_statesObject



261
262
263
264
# File 'lib/story_teller/game/loader.rb', line 261

def load_game_states
  states = game.config.fetch(:additional_promiscuous_states, []).map(&:to_sym)
  Session.add_promiscuous_states(states)
end

#load_game_sub(component, start = Time.now) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/story_teller/game/loader.rb', line 140

def load_game_sub(component, start = Time.now)
  game_component_path = File.join(@game_dir_path, component.to_s)
  Dir.glob(File.join(game_component_path, '*')).each do |file|
    next unless File.file?(file)
    log.debug "Loading file: #{file}"
    require file
  end
ensure
  elapsed = (Time.now - start) * 1000
  log.debug format("Loaded #{component} in %0.2f milliseconds", elapsed)
end

#load_game_subcomponents(components = game_components) ⇒ Object



115
116
117
# File 'lib/story_teller/game/loader.rb', line 115

def load_game_subcomponents(components = game_components)
  components.each { |component| load_game_sub(component) }
end

#load_grammarsObject



248
249
250
251
252
253
254
255
# File 'lib/story_teller/game/loader.rb', line 248

def load_grammars
  grammar_files.each do |file_path|
    start = Time.now
    StoryTeller::Library::Loader.load_grammar_by_path(file_path)
    elapsed = (Time.now - start) * 1000
    log.debug format(GrammarParsedInfoMessage, file: File.basename(file_path), elapsed: elapsed)
  end
end

#load_libraryObject



257
258
259
# File 'lib/story_teller/game/loader.rb', line 257

def load_library
  StoryTeller::Library::Loader.load_library
end

#load_path=(path) ⇒ Object



110
111
112
113
# File 'lib/story_teller/game/loader.rb', line 110

def load_path=(path)
  @game_dir_path = path
  $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
end

#post_game_componentsObject



123
124
125
# File 'lib/story_teller/game/loader.rb', line 123

def post_game_components
  game_components - pre_game_components
end

#pre_game_componentsObject



119
120
121
# File 'lib/story_teller/game/loader.rb', line 119

def pre_game_components
  game_components & PreGameComponents
end

#preserve_persisted_world?Boolean

Returns:

  • (Boolean)


174
175
176
# File 'lib/story_teller/game/loader.rb', line 174

def preserve_persisted_world?
  options[:persist] && persistence.world_tree?
end

#prioritize_if(list, &condition) ⇒ Object

Move to the front of the list any element matching the given condition



230
231
232
233
234
# File 'lib/story_teller/game/loader.rb', line 230

def prioritize_if(list, &condition)
  element = list.find(&condition)
  return list if element.nil?
  list.unshift(list.delete(element))
end

#refresh_preserved_world_object(object, preserved_ids) ⇒ Object



207
208
209
210
211
212
213
214
215
216
# File 'lib/story_teller/game/loader.rb', line 207

def refresh_preserved_world_object(object, preserved_ids)
  return if object.id.nil?

  unless preserved_ids.include?(object.id)
    raise GameLoadError, "Source object is missing from preserved world: #{object.object_name}"
  end

  object.associations.clear if object.respond_to?(:associations)
  object.refresh
end

#refresh_preserved_world_objects(preserved_ids) ⇒ Object



201
202
203
204
205
# File 'lib/story_teller/game/loader.rb', line 201

def refresh_preserved_world_objects(preserved_ids)
  ObjectSpace.each_object(Inform::Object) do |object|
    refresh_preserved_world_object(object, preserved_ids)
  end
end

#releaseObject



136
137
138
# File 'lib/story_teller/game/loader.rb', line 136

def release
  defined?(::Release) ? ::Release : 0
end

#reload_gameObject



266
267
268
# File 'lib/story_teller/game/loader.rb', line 266

def reload_game
  load_game
end

#reset_constantsObject



127
128
129
130
# File 'lib/story_teller/game/loader.rb', line 127

def reset_constants
  reset_constant(:HDR_GAMESERIAL, game_serial)
  reset_constant(:HDR_GAMERELEASE, release)
end

#same_path?(first, second) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/story_teller/game/loader.rb', line 102

def same_path?(first, second)
  File.expand_path(first) == File.expand_path(second)
end

#top_level_game_path(game_dir_path) ⇒ Object



96
97
98
99
100
# File 'lib/story_teller/game/loader.rb', line 96

def top_level_game_path(game_dir_path)
  return game_dir_path unless same_path?(game_dir_path, game.path)

  game.file_path || game_dir_path
end