inform-runtime 1.0.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -12
  3. data/Rakefile +26 -16
  4. data/lib/{runtime → story_teller}/articles.rb +14 -10
  5. data/lib/{runtime → story_teller}/builtins.rb +50 -22
  6. data/lib/{runtime → story_teller}/color.rb +8 -8
  7. data/lib/{runtime → story_teller}/command.rb +26 -28
  8. data/lib/{runtime → story_teller}/context.rb +23 -24
  9. data/lib/story_teller/core.rb +38 -0
  10. data/lib/{runtime → story_teller}/daemon.rb +35 -36
  11. data/lib/story_teller/engine.rb +151 -0
  12. data/lib/story_teller/ephemeral_adapter.rb +42 -0
  13. data/lib/{runtime → story_teller}/events.rb +8 -9
  14. data/lib/{runtime → story_teller}/experimental/handler_dsl.rb +7 -18
  15. data/lib/story_teller/experimental/reverse_engineer_class.rb +37 -0
  16. data/lib/{runtime → story_teller}/grammar_parser.rb +24 -40
  17. data/lib/{runtime → story_teller}/helpers.rb +21 -7
  18. data/lib/{runtime → story_teller}/history.rb +5 -5
  19. data/lib/{runtime → story_teller}/inflector.rb +4 -5
  20. data/lib/story_teller/inform/base.rb +160 -0
  21. data/lib/{runtime → story_teller/inform/ephemeral}/link.rb +27 -45
  22. data/lib/{runtime → story_teller/inform/ephemeral}/module.rb +17 -58
  23. data/lib/story_teller/inform/ephemeral/object.rb +329 -0
  24. data/lib/{runtime → story_teller/inform/ephemeral}/tag.rb +54 -81
  25. data/lib/story_teller/inform/models.rb +25 -0
  26. data/lib/{runtime → story_teller}/io.rb +10 -10
  27. data/lib/{runtime → story_teller}/kernel.rb +21 -31
  28. data/lib/story_teller/library/bootstrap.rb +66 -0
  29. data/lib/story_teller/library/declarations.rb +53 -0
  30. data/lib/story_teller/library/directives.rb +91 -0
  31. data/lib/story_teller/library/loader.rb +104 -0
  32. data/lib/story_teller/library/location.rb +73 -0
  33. data/lib/{runtime → story_teller}/library.rb +27 -12
  34. data/lib/{runtime → story_teller}/logging.rb +47 -24
  35. data/lib/{runtime → story_teller}/mixins.rb +6 -6
  36. data/lib/story_teller/model_adapter.rb +132 -0
  37. data/lib/{runtime → story_teller}/plurals.rb +11 -11
  38. data/lib/{runtime → story_teller}/prototype.rb +11 -10
  39. data/lib/{runtime → story_teller}/publication.rb +9 -9
  40. data/lib/{runtime → story_teller}/session.rb +6 -8
  41. data/lib/{runtime → story_teller}/stdlib.rb +13 -11
  42. data/lib/{runtime → story_teller}/subscription.rb +8 -8
  43. data/lib/{runtime → story_teller}/tree.rb +6 -6
  44. data/lib/{runtime → story_teller}/version.rb +16 -6
  45. data/lib/story_teller/world_tree.rb +54 -0
  46. data/lib/story_teller.rb +26 -0
  47. metadata +59 -99
  48. data/config/database.yml +0 -37
  49. data/exe/inform.rb +0 -6
  50. data/game/config.yml +0 -5
  51. data/game/example.inf +0 -76
  52. data/game/example.rb +0 -90
  53. data/game/forms/example_form.rb +0 -2
  54. data/game/grammar/game_grammar.inf.rb +0 -11
  55. data/game/languages/english.rb +0 -2
  56. data/game/models/example_model.rb +0 -2
  57. data/game/modules/example_module.rb +0 -9
  58. data/game/rules/example_state.rb +0 -2
  59. data/game/scripts/example_script.rb +0 -2
  60. data/game/topics/example_topic.rb +0 -2
  61. data/game/verbs/game_verbs.rb +0 -15
  62. data/game/verbs/metaverbs.rb +0 -2028
  63. data/lib/runtime/config.rb +0 -48
  64. data/lib/runtime/database.rb +0 -500
  65. data/lib/runtime/game.rb +0 -74
  66. data/lib/runtime/game_loader.rb +0 -132
  67. data/lib/runtime/library_loader.rb +0 -135
  68. data/lib/runtime/object.rb +0 -761
  69. data/lib/runtime/options.rb +0 -104
  70. data/lib/runtime/persistence.rb +0 -292
  71. data/lib/runtime/runtime.rb +0 -321
  72. data/lib/runtime/world_tree.rb +0 -69
  73. data/lib/runtime.rb +0 -35
@@ -1,104 +0,0 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: false
3
-
4
- # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
- #
6
- # This file is part of the Inform Runtime.
7
- #
8
- # The Inform Runtime is free software: you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License as published
10
- # by the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # The Inform Runtime is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
-
21
- require 'logger'
22
- require 'optparse'
23
-
24
- require_relative 'version'
25
-
26
- # The Inform module
27
- module Inform
28
- # The Options module
29
- module Options
30
- # The ArgumentsParser
31
- class ArgumentsParser
32
- attr_reader :parser, :options
33
-
34
- def initialize(args, option_parser = OptionParser.new, **params)
35
- @parser = option_parser
36
- @options = ::Inform::Runtime.default_config.dup
37
- flags.each { |method_name| self.method(method_name).call }
38
- @parser.parse!(args, **params)
39
- end
40
-
41
- def flags
42
- @flags ||= %i[banner admin log_level help version]
43
- end
44
-
45
- def banner
46
- @parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [game_path] [options]"
47
- @parser.separator ''
48
- @parser.separator 'Arguments:'
49
- @parser.separator ' game_path Path to game file or directory'
50
- @parser.separator ''
51
- @parser.separator 'Options:'
52
- end
53
-
54
- def admin
55
- @parser.on_tail('--admin', 'Set player character as admin; default: false') do
56
- @options[:admin] = true
57
- end
58
- end
59
-
60
- def log_level
61
- @parser.on_tail('-v', '--verbose', 'Increase verbosity') do
62
- @options[:log_level] ||= Logger::INFO
63
- @options[:log_level] -= 1
64
- end
65
- end
66
-
67
- def help
68
- @parser.on_tail('-?', '--help', 'Show this message') do
69
- puts @parser
70
- exit
71
- end
72
- end
73
-
74
- def version
75
- @parser.on_tail('--version', 'Show version') do
76
- puts "#{$PROGRAM_NAME} version #{Inform::VERSION}"
77
- exit
78
- end
79
- end
80
- end
81
- # class ArgumentsParser
82
-
83
- def demand(options, arg, positional = false)
84
- return options[arg] unless options[arg].nil?
85
- required_arg = positional ? "<#{arg}>" : "--#{arg.to_s.gsub(/_/, '-')}"
86
- raise UserError, "Required argument: #{required_arg}"
87
- end
88
-
89
- def parse_arguments(args = ARGV, _file_path = ARGF)
90
- arguments_parser = ArgumentsParser.new(args)
91
- demand(arguments_parser.options, :game_path)
92
- arguments_parser.options
93
- rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
94
- OptionParser::MissingArgument, OptionParser::NeedlessArgument => e
95
- puts e.message
96
- puts arguments_parser.parser
97
- exit
98
- rescue OptionParser::AmbiguousOption => e
99
- abort e.message
100
- end
101
- end
102
- # module Options
103
- end
104
- # module Inform
@@ -1,292 +0,0 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: false
3
-
4
- # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
- #
6
- # This file is part of the Inform Runtime.
7
- #
8
- # The Inform Runtime is free software: you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License as published
10
- # by the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # The Inform Runtime is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
-
21
- require 'etc'
22
- require 'logger'
23
- require 'uri'
24
- require 'yaml'
25
-
26
- require 'sequel'
27
-
28
- require_relative 'database'
29
-
30
- Sequel.extension :migration
31
- Sequel.extension :connection_validator
32
-
33
- # Re-open the Inform module to define a helper method
34
- module Inform
35
- def self.initialize_persistence_layer
36
- log.debug "Inform.initialize_persistence_layer"
37
- Inform::Persistence.init
38
- end
39
- end
40
-
41
- # Simplistic in-memory cache
42
- class EphemeralCache < Hash
43
- def get(key)
44
- self[key]
45
- end
46
- def set(key, value, _ttl)
47
- self[key] = value
48
- end
49
- end
50
-
51
- # The Persistence class
52
- class Persistence
53
- unless defined?(Persistence::GlobalCache)
54
- GlobalCache = defined?(Java) ? java.util.concurrent.ConcurrentHashMap.new : EphemeralCache.new
55
- end
56
- Plugins = {
57
- after_initialize: [],
58
- caching: [Persistence::GlobalCache],
59
- json_serializer: [],
60
- xml_serializer: []
61
- # tactical_eager_loading: [],
62
- # eager_each: []
63
- }.freeze
64
- end
65
-
66
- # The SequelLoggers module
67
- module SequelLoggers
68
- DatabaseLogging = Struct.new(:memo).new({})
69
-
70
- def add_logger(database, logger)
71
- database.loggers << logger unless database.loggers.include?(logger)
72
- end
73
-
74
- def delete_logger(database, logger)
75
- database.loggers.delete(logger) if database.loggers.include?(logger)
76
- end
77
-
78
- def set_log_level(database, level)
79
- DatabaseLogging.memo[:preserved_log_levels][database] = database.sql_log_level
80
- database.sql_log_level = level unless level.nil?
81
- end
82
-
83
- def enable_query_logging
84
- DatabaseLogging.memo[:logger] ||= Logger.new($stdout)
85
- DatabaseLogging.memo[:preserved_log_levels] ||= {}
86
- logger = DatabaseLogging.memo[:logger]
87
- Sequel::DATABASES.each do |database|
88
- add_logger(database, logger)
89
- set_log_level(database, :debug)
90
- logger.debug "Enabled query logging for #{database}"
91
- end
92
- end
93
-
94
- def disable_query_logging
95
- logger = DatabaseLogging.memo[:logger]
96
- Sequel::DATABASES.each do |database|
97
- logger.debug "Disabling query logging for #{database}"
98
- delete_logger(database, logger)
99
- set_log_level(database, DatabaseLogging.memo[:preserved_log_levels][database])
100
- end
101
- end
102
- end
103
- # module SequelLoggers
104
-
105
- # The SequelPlugins module
106
- module SequelPlugins
107
- def enable_plugins
108
- Persistence::Plugins.each { |plugin, parameters| enable_plugin(plugin, parameters) }
109
- end
110
-
111
- def enable_plugin(plugin, parameters = [])
112
- return Sequel::Model.plugin(plugin) if parameters.empty?
113
- Sequel::Model.plugin(plugin, *parameters)
114
- rescue LoadError => e
115
- log.error e.message
116
- end
117
- end
118
- # module SequelPlugins
119
-
120
- # The Inform module
121
- module Inform
122
- # The Persistence class
123
- class Persistence
124
- include DatabaseConnectionHelpers
125
- include SequelLoggers
126
- include SequelPlugins
127
-
128
- attr_reader :config, :environment
129
-
130
- def self.init(env = nil)
131
- Inform::Persistence.instance(env)
132
- end
133
-
134
- @instance_mutex = Mutex.new
135
-
136
- def self.instance(*args)
137
- return @instance unless @instance.nil?
138
- @instance_mutex.synchronize do
139
- @instance ||= new(*args)
140
- end
141
- @instance
142
- end
143
-
144
- private_class_method :new
145
-
146
- def initialize(env = nil)
147
- @environment = env || Inform::Game.environment
148
- log.debug "Initializing persistence layer for environment: #{@environment}"
149
- caller[0..4].each { |t| log.debug t }
150
- @config = database_config.fetch(@environment.to_s, {})
151
- establish_database_connection
152
- enable_plugins
153
- enable_query_logging if ENV['ENABLE_SQL_LOGGING']
154
- end
155
-
156
- def establish_database_connection
157
- connect
158
- rescue Sequel::DatabaseConnectionError
159
- log.warn "Database requires initialization"
160
- init_database
161
- retry if ConnectionAttempts.memo < 2
162
- end
163
-
164
- def database_config
165
- @database_config ||= YAML.load_file(database_config_file_path)
166
- end
167
-
168
- def database_config_file_path
169
- @database_config_file_path ||= File.expand_path(
170
- File.join(Inform::Runtime.project_dir_path, 'config', 'database.yml')
171
- )
172
- end
173
-
174
- def init_database
175
- @config = database_config.fetch('default')
176
- connect
177
- @database = Inform::Database.instance(Inform::Game.config[:database_name])
178
- @database.bootstrap.up
179
- @config = database_config.fetch(environment.to_s)
180
- connect
181
- end
182
- end
183
- # class Persistence
184
- end
185
- # module Inform
186
-
187
- # The Inform module
188
- module Inform
189
- # The ImplicitMigration module
190
- module ImplicitMigration
191
- NoDatabasePattern = %r{No database associated with Sequel::Model}.freeze
192
- MigrationSetupTemplate = '%<model>sSetup'.freeze
193
- ModuleNamespaceDelimiterPattern = /::/.freeze
194
-
195
- def before_inherited(subclass)
196
- return if self != Sequel::Model
197
- descendants << subclass
198
- log.debug "#{subclass} << #{self} [#{descendants}]"
199
- maybe_migrate(subclass)
200
- end
201
-
202
- def after_inherited(_subclass)
203
- examine_schema
204
- end
205
-
206
- # rubocop: disable Metrics/AbcSize
207
- # rubocop: disable Metrics/MethodLength
208
- def examine_schema
209
- descendants.each do |model_class|
210
- table_name = model_class.table_name
211
- indexes = self.db.indexes(table_name)
212
- columns = model_class.columns
213
- associations = model_class.associations
214
-
215
- log.debug "Table: #{table_name}"
216
- log.debug "Columns: #{columns.join(', ')}"
217
- log.debug "Indexes: #{indexes}"
218
-
219
- associations.each do |assoc_name, assoc_data|
220
- log.debug "Association: #{assoc_name} (#{assoc_data[:type]}) to #{assoc_data[:class_name]}"
221
- end
222
-
223
- log.debug "==========="
224
- end
225
- end
226
- # rubocop: enable Metrics/AbcSize
227
- # rubocop: enable Metrics/MethodLength
228
-
229
- def maybe_migrate(subclass)
230
- migration_class = migration(subclass)
231
- log.debug "Found migration class: #{migration_class}"
232
- migration_class&.up
233
- rescue Sequel::Error => e
234
- if NoDatabasePattern.match?(e.message)
235
- Inform::Persistence.instance.connect
236
- retry
237
- end
238
- end
239
-
240
- def migration(model)
241
- names = format(MigrationSetupTemplate, model: model).split(ModuleNamespaceDelimiterPattern)
242
- names.inject(Object) do |mod, class_name|
243
- mod.const_get(class_name)
244
- rescue StandardError => e
245
- log.warn "Error getting reference to migration model class: #{e.message}"
246
- next
247
- end
248
- end
249
- end
250
- # module ImplicitMigration
251
- end
252
- # module Inform
253
-
254
- # The Inform module
255
- module Inform
256
- # The InheritanceListener module
257
- module InheritanceListener
258
- # The ClassMethods module
259
- module ClassMethods
260
- include Inform::ImplicitMigration
261
-
262
- def inherited(subclass)
263
- before_inherited(subclass)
264
- super
265
- after_inherited(subclass)
266
- end
267
-
268
- # Returns the list of Model descendants.
269
- def descendants
270
- @descendants ||= []
271
- end
272
- end
273
-
274
- def self.included(base)
275
- base.extend(ClassMethods)
276
- end
277
- end
278
- # module InheritanceListener
279
- end
280
- # module Inform
281
-
282
- # The Sequel module
283
- module Sequel
284
- # The Sequel::Model class
285
- class Model
286
- include Inform::InheritanceListener
287
-
288
- def self.implicit_table_name
289
- underscore(demodulize(name)).to_sym
290
- end
291
- end
292
- end
@@ -1,321 +0,0 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: false
3
-
4
- # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
- #
6
- # This file is part of the Inform Runtime.
7
- #
8
- # The Inform Runtime is free software: you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License as published
10
- # by the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # The Inform Runtime is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
-
21
- require_relative 'config'
22
- require_relative 'game'
23
- require_relative 'session'
24
- require_relative 'io'
25
- require_relative 'context'
26
- require_relative 'library'
27
- require_relative 'options'
28
- require_relative 'publication'
29
-
30
- # The Inform module
31
- module Inform
32
- # The RuntimeConstants module
33
- module RuntimeConstants
34
- UndefinedMainPattern = %r{undefined method 'Main' for an instance of Object}.freeze
35
- Registry = Class.new(Hash)
36
- LibraryRegistry = Registry.new
37
- end
38
-
39
- # The RuntimeLibrary module
40
- module RuntimeLibrary
41
- def play
42
- log.debug "Using runtime-managed InformLibrary play method override"
43
- inform_library = Inform::Runtime.libraries[Inform::Runtime]
44
- inform_library.play
45
- inform_library
46
- end
47
- end
48
-
49
- # The RuntimeClassMethods module
50
- module RuntimeClassMethods
51
- def libraries
52
- Inform::Runtime::LibraryRegistry
53
- end
54
-
55
- def default_config
56
- @default_config ||= Inform::Config::DEFAULTS
57
- end
58
-
59
- def default_game_dir_path
60
- File.join(project_dir_path, Inform::Runtime.default_config[:game_dir_name])
61
- end
62
-
63
- def support_dir_path
64
- Inform::SUPPORT_DIR_PATH
65
- end
66
-
67
- def inform_dir_path
68
- Inform::INFORM_DIR_PATH
69
- end
70
-
71
- def lib_dir_path
72
- Inform::LIB_DIR_PATH
73
- end
74
-
75
- def project_dir_path
76
- Inform::PROJECT_DIR_PATH
77
- end
78
-
79
- def database_saves_dir_path
80
- File.join(Inform::Runtime.project_dir_path, '.' + Inform::Game.config[:database_name] + '_saves')
81
- end
82
-
83
- def default_environment
84
- Inform::Runtime.default_config[:environment]
85
- end
86
-
87
- def invocation_properties
88
- return Inform::Runtime.instance.invocation_properties unless Inform::Runtime.instance.nil?
89
- Inform::Runtime.default_config.fetch(:properties, '').split.map(&:to_sym)
90
- end
91
-
92
- def invocation_context
93
- return Inform::Runtime.instance.invocation_context unless Inform::Runtime.instance.nil?
94
- Struct.new(*Inform::Runtime.invocation_properties)
95
- end
96
-
97
- def language_name
98
- Inform::Runtime.default_config[:language]
99
- end
100
-
101
- def main_gem_spec_executable
102
- require 'rubygems'
103
- current_working_dir = File.expand_path(__dir__)
104
- gem_spec = Gem.loaded_specs.values.find do |s|
105
- current_working_dir.start_with?(s.full_gem_path)
106
- end
107
- (gem_spec&.executables || []).first
108
- end
109
-
110
- def player?(obj)
111
- return false unless defined?(Inform::IO::Session) && Inform::IO::Session.respond_to?(:players)
112
- Inform::IO::Session.players.include?(obj)
113
- end
114
-
115
- def init(options = Inform::Runtime.default_config)
116
- Inform::Runtime.instance(options)
117
- end
118
-
119
- def instance(options = Inform::Runtime.default_config)
120
- return @instance unless @instance.nil?
121
- @instance_mutex.synchronize do
122
- @instance ||= new(options)
123
- end
124
- @instance
125
- end
126
- end
127
- # module RuntimeClassMethods
128
-
129
- # The RuntimeInstanceMethods module
130
- module RuntimeInstanceMethods
131
- include RuntimeConstants
132
-
133
- attr_accessor :main_object
134
-
135
- def config_file_path
136
- @config_file_path ||= File.join(game_path, @options[:game_config_file_name])
137
- end
138
-
139
- def game_dir_name
140
- @game_dir_name ||= @options[:game_dir_name]
141
- end
142
-
143
- def game_path
144
- @game_path ||= File.expand_path(@options[:game_path])
145
- end
146
-
147
- def game_components
148
- @game_components ||= @options.fetch(:game_components, '').split.map(&:to_sym)
149
- end
150
-
151
- def grammar_module_path
152
- @grammar_module_path ||= File.join(game_path, @options[:game_grammar_module_name])
153
- end
154
-
155
- def invocation_properties
156
- @invocation_properties ||= @options.fetch(:properties, '').split.map(&:to_sym)
157
- end
158
-
159
- def invocation_context
160
- @invocation_context ||= Struct.new(*invocation_properties)
161
- end
162
-
163
- def invoke_main_method
164
- ::Object.new.send(:Main)
165
- rescue NameError => e
166
- if UndefinedMainPattern.match?(e.message)
167
- log.error "Main() definition is missing"
168
- else
169
- log.error e.message
170
- e.backtrace.each { |t| log.error t } # TODO: Remove
171
- end
172
- end
173
-
174
- # TODO: Ensuring the location of the player object requires
175
- # that this method be invoked before the move @player, @location
176
- # operation in the InformLibrary#play method. This means that
177
- # ensuring the location must take place either in the game-
178
- # defined Initialise() method, or else it can happen in a
179
- # provided LibraryExtension method, but in that case it would
180
- # be at risk of being overridden by a game-defined Initialise()
181
- # method.
182
- # TODO: Try to figure out what the best approach here is. I
183
- # think that if a multi-player game is to supply an entrypoint
184
- # module, it will have to handle the location ensurance itself.
185
- def ensure_location
186
- return if inform_library.nil?
187
- return if inform_library.location
188
- player_obj = inform_library.player
189
- location = player_obj.location
190
- location ||= player_obj.spawn_point if player_obj.respond_to?(:spawn_point)
191
- inform_library.location = location
192
- end
193
-
194
- def inform_library(key = Inform::Runtime)
195
- Inform::Runtime.libraries[key]
196
- end
197
-
198
- def manage_inform_library(key = Inform::Runtime)
199
- Inform::Runtime.libraries[key] ||= begin
200
- inform_library = InformLibrary.new
201
- if self.respond_to?(:specialized_player)
202
- inform_library.selfobj = specialized_player
203
- end
204
- inform_library.subscribe(inform_library.selfobj)
205
- inform_library
206
- end
207
- end
208
-
209
- def apply_preferences
210
- return if inform_library.nil?
211
- player_obj = inform_library.player
212
- preferences = player_obj.&:preferences
213
- return if preferences.nil? || !preferences.respond_to?(:properties)
214
- preferences.properties.each_pair do |key, value|
215
- variable = :"@#{key}"
216
- Parser.initial_state.delete(variable)
217
- inform_library.instance_variable_set(variable, value)
218
- end
219
- end
220
-
221
- def manage_privileges
222
- Inform::Object.include(Inform::Privileges)
223
- return unless @options[:admin]
224
- give inform_library.player, :admin
225
- end
226
-
227
- def read_eval_print_loop
228
- loop do
229
- prompt
230
- inform_library.send(inform_library.inform(read))
231
- end
232
- rescue Interrupt => e
233
- $stdout.puts "\n#{e.class.name}"
234
- end
235
-
236
- def play_game
237
- log.debug "#{self}#play_game"
238
- invoke_main_method
239
- # ensure_location
240
- apply_preferences
241
- manage_privileges
242
- read_eval_print_loop
243
- end
244
-
245
- def quit
246
- $stdout.print "[Hit enter to exit.]"
247
- $stdin.getc
248
- # Curses.getch
249
- # Curses.close_screen
250
- exit
251
- end
252
-
253
- def to_s
254
- "#<#{self.class.name}:#{object_id}>"
255
- end
256
-
257
- def inspect
258
- to_s
259
- end
260
- end
261
- # module RuntimeInstanceMethods
262
-
263
- # The Inform::Privileges module to implement privilege
264
- module Privileges
265
- def admin?
266
- self.has?(:admin)
267
- end
268
-
269
- def builder?
270
- admin? || self.has?(:builder)
271
- end
272
- end
273
-
274
- # The Runtime class loads a game and plays it in a REPL session.
275
- class Runtime
276
- include Inform::RuntimeConstants
277
- include Inform::RuntimeInstanceMethods
278
- include Inform::Game::Loader
279
- include Inform::Library::Loader
280
- include Inform::IO
281
- include Inform::Publisher
282
-
283
- class << self
284
- include Inform::RuntimeClassMethods
285
- include Inform::Library::ClassMethods
286
- end
287
-
288
- @default_config = nil
289
- @instance = nil
290
- @instance_mutex = Mutex.new
291
-
292
- private_class_method :new
293
-
294
- def initialize(options = Inform::Runtime.default_config)
295
- @options = options
296
- Inform::Game.init(@options)
297
- Inform::Context.set(invocation_context)
298
- Inform.initialize_persistence_layer
299
- end
300
- end
301
- # class Runtime
302
-
303
- # The App module
304
- module App
305
- include Inform::Options
306
-
307
- def main(args = parse_arguments)
308
- Logging.log_level = args[:log_level]
309
- runtime = Inform::Runtime.init(args)
310
- runtime.load_game
311
- runtime.play_game
312
- end
313
- end
314
- end
315
- # module Inform
316
-
317
- if defined?(ZCODE_ONLY)
318
- # Simulate compiling with Z-code only compiler
319
- WORDSIZE = (2**((0.size * 8) - 2) - 1)
320
- end
321
- # defined?(ZCODE_ONLY)