Module: Lich::Gemstone::Infomon
- Defined in:
- lib/gemstone/infomon.rb,
lib/gemstone/infomon/cli.rb,
lib/gemstone/infomon/cache.rb,
lib/gemstone/infomon/parser.rb,
lib/gemstone/infomon/xmlparser.rb
Defined Under Namespace
Modules: Parser, XMLParser Classes: Cache
Constant Summary collapse
Class Method Summary collapse
-
._key(key) ⇒ String
Normalizes the key by converting it to a string and formatting it.
-
._validate!(key, value) ⇒ Object
Validates the key and value types for insertion.
-
._value(val) ⇒ Boolean, Object
Normalizes the value to a boolean or returns the original value.
-
.cache ⇒ Infomon::Cache
Returns the cache object used by Infomon.
-
.cache_load ⇒ void
Loads the cache from the database.
-
.context! ⇒ Object
Ensures that the context is valid before accessing Infomon.
-
.db ⇒ Sequel::Database
Returns the database connection.
-
.db_refresh_needed? ⇒ Boolean
Checks if a refresh of the Infomon database is needed.
-
.delete!(key) ⇒ void
Deletes a key from the cache and database.
-
.file ⇒ String
Returns the file path for the database.
-
.get(key) ⇒ Boolean, ...
Retrieves a value from the cache or database.
-
.get_bool(key) ⇒ Boolean
Retrieves a boolean value from the cache or database.
-
.mutex ⇒ Mutex
Returns the mutex used for SQL operations.
-
.mutex_lock ⇒ Object
Locks the mutex for thread-safe operations.
-
.mutex_unlock ⇒ Object
Unlocks the mutex for thread-safe operations.
-
.queue ⇒ Queue
Returns the SQL queue for pending operations.
-
.redo! ⇒ void
Resets the Infomon data for the character.
-
.reset! ⇒ void
Resets the Infomon state by dropping the table and clearing the cache.
-
.set(key, value) ⇒ Symbol
Sets a key-value pair in the cache and database.
-
.setup! ⇒ Sequel::Dataset
Sets up the Infomon table in the database.
-
.show(full = false) ⇒ Array<String>
Displays stored Infomon data for the character.
-
.sync ⇒ void
CLI commands for Infomon.
-
.table ⇒ Sequel::Dataset
Returns the table object for the Infomon database.
-
.table_name ⇒ Symbol
Returns the table name based on the game and XMLData name.
-
.upsert(*args) ⇒ void
Inserts or updates a record in the database.
-
.upsert_batch(*blob) ⇒ Symbol
Inserts or updates multiple records in the database.
Class Method Details
._key(key) ⇒ String
Normalizes the key by converting it to a string and formatting it.
156 157 158 159 160 |
# File 'lib/gemstone/infomon.rb', line 156 def self._key(key) key = key.to_s.downcase key.tr!(' ', '_').gsub!('_-_', '_').tr!('-', '_') if /\s|-/.match?(key) return key end |
._validate!(key, value) ⇒ Object
Validates the key and value types for insertion.
179 180 181 182 |
# File 'lib/gemstone/infomon.rb', line 179 def self._validate!(key, value) return self._value(value) if AllowedTypes.include?(value.class) raise "infomon:insert(%s) was called with %s\nmust be %s\nvalue=%s" % [key, value.class, AllowedTypes.map(&:name).join("|"), value] end |
._value(val) ⇒ Boolean, Object
Normalizes the value to a boolean or returns the original value.
166 167 168 169 170 |
# File 'lib/gemstone/infomon.rb', line 166 def self._value(val) return true if val.to_s == "true" return false if val.to_s == "false" return val end |
.cache ⇒ Infomon::Cache
Returns the cache object used by Infomon.
36 37 38 |
# File 'lib/gemstone/infomon.rb', line 36 def self.cache @cache end |
.cache_load ⇒ void
This method will sleep for a short duration if XMLData.name is not loaded.
This method returns an undefined value.
Loads the cache from the database.
144 145 146 147 148 149 150 |
# File 'lib/gemstone/infomon.rb', line 144 def self.cache_load sleep(0.01) if XMLData.name.empty? dataset = Infomon.table h = Hash[dataset.map(:key).zip(dataset.map(:value))] self.cache.merge!(h) @cache_loaded = true end |
.context! ⇒ Object
Ensures that the context is valid before accessing Infomon.
95 96 97 98 99 |
# File 'lib/gemstone/infomon.rb', line 95 def self.context! return unless XMLData.name.empty? or XMLData.name.nil? puts Exception.new.backtrace fail "cannot access Infomon before XMLData.name is loaded" end |
.db ⇒ Sequel::Database
Returns the database connection.
50 51 52 |
# File 'lib/gemstone/infomon.rb', line 50 def self.db @db end |
.db_refresh_needed? ⇒ Boolean
Checks if a refresh of the Infomon database is needed.
95 96 97 98 |
# File 'lib/gemstone/infomon/cli.rb', line 95 def self.db_refresh_needed? # Change date below to the last date of infomon.db structure change to allow for a forced reset of data. Infomon.get("infomon.last_sync").nil? || Infomon.get("infomon.last_sync") < Time.new(2024, 8, 5, 20, 0, 0).to_i end |
.delete!(key) ⇒ void
This method returns an undefined value.
Deletes a key from the cache and database.
268 269 270 271 272 |
# File 'lib/gemstone/infomon.rb', line 268 def self.delete!(key) key = self._key(key) self.cache.delete(key) self.queue << "DELETE FROM %s WHERE key = (%s);" % [self.db.literal(self.table_name), self.db.literal(key)] end |
.file ⇒ String
Returns the file path for the database.
43 44 45 |
# File 'lib/gemstone/infomon.rb', line 43 def self.file @file end |
.get(key) ⇒ Boolean, ...
Retrieves a value from the cache or database.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/gemstone/infomon.rb', line 191 def self.get(key) self.cache_load if !@cache_loaded key = self._key(key) val = self.cache.get(key) { sleep 0.01 until self.queue.empty? begin self.mutex.synchronize do begin db_result = self.table[key: key] if db_result db_result[:value] else nil end rescue => exception pp(exception) nil end end rescue StandardError respond "--- Lich: error: Infomon.get(#{key}): #{$!}" Lich.log "error: Infomon.get(#{key}): #{$!}\n\t#{$!.backtrace.join("\n\t")}" end } return self._value(val) end |
.get_bool(key) ⇒ Boolean
Retrieves a boolean value from the cache or database.
224 225 226 227 228 229 230 231 232 233 |
# File 'lib/gemstone/infomon.rb', line 224 def self.get_bool(key) value = Infomon.get(key) if value.is_a?(TrueClass) || value.is_a?(FalseClass) return value elsif value == 1 return true else return false end end |
.mutex ⇒ Mutex
Returns the mutex used for SQL operations.
57 58 59 |
# File 'lib/gemstone/infomon.rb', line 57 def self.mutex @sql_mutex end |
.mutex_lock ⇒ Object
Locks the mutex for thread-safe operations.
64 65 66 67 68 69 70 71 |
# File 'lib/gemstone/infomon.rb', line 64 def self.mutex_lock begin self.mutex.lock unless self.mutex.owned? rescue StandardError respond "--- Lich: error: Infomon.mutex_lock: #{$!}" Lich.log "error: Infomon.mutex_lock: #{$!}\n\t#{$!.backtrace.join("\n\t")}" end end |
.mutex_unlock ⇒ Object
Unlocks the mutex for thread-safe operations.
76 77 78 79 80 81 82 83 |
# File 'lib/gemstone/infomon.rb', line 76 def self.mutex_unlock begin self.mutex.unlock if self.mutex.owned? rescue StandardError respond "--- Lich: error: Infomon.mutex_unlock: #{$!}" Lich.log "error: Infomon.mutex_unlock: #{$!}\n\t#{$!.backtrace.join("\n\t")}" end end |
.queue ⇒ Queue
Returns the SQL queue for pending operations.
88 89 90 |
# File 'lib/gemstone/infomon.rb', line 88 def self.queue @sql_queue end |
.redo! ⇒ void
This method returns an undefined value.
Resets the Infomon data for the character.
This method deletes the character table, recreates it, and repopulates it.
59 60 61 62 63 64 65 |
# File 'lib/gemstone/infomon/cli.rb', line 59 def self.redo! # Destructive - deletes char table, recreates it, then repopulates it respond 'Infomon complete reset reqeusted.' Infomon.reset! Infomon.sync respond 'Infomon reset is now complete.' end |
.reset! ⇒ void
This method returns an undefined value.
Resets the Infomon state by dropping the table and clearing the cache.
112 113 114 115 116 117 118 |
# File 'lib/gemstone/infomon.rb', line 112 def self.reset! self.mutex_lock Infomon.db.drop_table?(self.table_name) self.cache.clear @cache_loaded = false Infomon.setup! end |
.set(key, value) ⇒ Symbol
Sets a key-value pair in the cache and database.
253 254 255 256 257 258 259 260 |
# File 'lib/gemstone/infomon.rb', line 253 def self.set(key, value) key = self._key(key) value = self._validate!(key, value) return :noop if self.cache.get(key) == value self.cache.put(key, value) self.queue << "INSERT OR REPLACE INTO %s (`key`, `value`) VALUES (%s, %s) on conflict(`key`) do update set value = excluded.value;" % [self.db.literal(self.table_name), self.db.literal(key), self.db.literal(value)] end |
.setup! ⇒ Sequel::Dataset
Sets up the Infomon table in the database.
130 131 132 133 134 135 136 137 138 |
# File 'lib/gemstone/infomon.rb', line 130 def self.setup! self.mutex_lock @db.create_table?(self.table_name) do text :key, primary_key: true any :value end self.mutex_unlock @_table = @db[self.table_name] end |
.show(full = false) ⇒ Array<String>
Displays stored Infomon data for the character.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/gemstone/infomon/cli.rb', line 73 def self.show(full = false) response = [] # display all stored db values respond "Displaying stored information for #{XMLData.name}" Infomon.table.map([:key, :value]).each { |k, v| response << "#{k} : #{v.inspect}\n" } unless full response.each { |_line| response.reject! do |line| line.match?(/\s:\s0$/) end } end respond response end |
.sync ⇒ void
This method returns an undefined value.
CLI commands for Infomon
This method synchronizes the character’s Infomon settings.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/gemstone/infomon/cli.rb', line 14 def self.sync # since none of this information is 3rd party displayed, silence is golden. shroud_detected = false respond 'Infomon sync requested.' if Effects::Spells.active?(1212) respond 'ATTENTION: SHROUD DETECTED - disabling Shroud of Deception to sync character\'s infomon setting' while Effects::Spells.active?(1212) dothistimeout('STOP 1212', 3, /^With a moment's concentration, you terminate the Shroud of Deception spell\.$|^Stop what\?$/) sleep(0.5) end shroud_detected = true end request = { 'info' => /<a exist=.+#{XMLData.name}/, 'skill' => /<a exist=.+#{XMLData.name}/, 'spell' => %r{<output class="mono"/>}, 'experience' => %r{<output class="mono"/>}, 'society' => %r{<pushBold/>}, 'citizenship' => /^You don't seem|^You currently have .+ in/, 'armor list all' => /<a exist=.+#{XMLData.name}/, 'cman list all' => /<a exist=.+#{XMLData.name}/, 'feat list all' => /<a exist=.+#{XMLData.name}/, 'shield list all' => /<a exist=.+#{XMLData.name}/, 'weapon list all' => /<a exist=.+#{XMLData.name}/, 'ascension list all' => /<a exist=.+#{XMLData.name}/, 'resource' => /^Health: \d+\/(?:<pushBold\/>)?\d+(?:<popBold\/>)?\s+Mana: \d+\/(?:<pushBold\/>)?\d+(?:<popBold\/>)?\s+Stamina: \d+\/(?:<pushBold\/>)?\d+(?:<popBold\/>)?\s+Spirit: \d+\/(?:<pushBold\/>)?\d+/, 'warcry' => /^You have learned the following War Cries:|^You must be an active member of the Warrior Guild to use this skill/ } request.each do |command, start_capture| respond "Retrieving character #{command}." if $infomon_debug Lich::Util.issue_command(command.to_s, start_capture, /<prompt/, include_end: true, timeout: 5, silent: false, usexml: true, quiet: true) respond "Did #{command}." if $infomon_debug end respond 'Requested Infomon sync complete.' respond 'ATTENTION: TEND TO YOUR SHROUD!' if shroud_detected Infomon.set('infomon.last_sync', Time.now.to_i) end |
.table ⇒ Sequel::Dataset
Returns the table object for the Infomon database.
123 124 125 |
# File 'lib/gemstone/infomon.rb', line 123 def self.table @_table ||= self.setup! end |
.table_name ⇒ Symbol
Returns the table name based on the game and XMLData name.
104 105 106 107 |
# File 'lib/gemstone/infomon.rb', line 104 def self.table_name self.context! ("%s_%s" % [XMLData.game, XMLData.name]).to_sym end |
.upsert(*args) ⇒ void
This method returns an undefined value.
Inserts or updates a record in the database.
239 240 241 242 243 |
# File 'lib/gemstone/infomon.rb', line 239 def self.upsert(*args) self.table .insert_conflict(:replace) .insert(*args) end |
.upsert_batch(*blob) ⇒ Symbol
Inserts or updates multiple records in the database.
281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/gemstone/infomon.rb', line 281 def self.upsert_batch(*blob) updated = (blob.first.map { |k, v| [self._key(k), self._validate!(k, v)] } - self.cache.to_a) return :noop if updated.empty? pairs = updated.map { |key, value| (value.is_a?(Integer) or value.is_a?(String)) or fail "upsert_batch only works with Integer or String types" # add the value to the cache self.cache.put(key, value) %[(%s, %s)] % [self.db.literal(key), self.db.literal(value)] }.join(", ") # queue sql statement to run async self.queue << "INSERT OR REPLACE INTO %s (`key`, `value`) VALUES %s on conflict(`key`) do update set value = excluded.value;" % [self.db.literal(self.table_name), pairs] end |