Module: Lich::DragonRealms::DRExpMonitor

Defined in:
documented/dragonrealms/drinfomon/drexpmonitor.rb

Constant Summary collapse

MAX_SQLITE_RETRIES =

Maximum SQLite retry attempts before giving up (prevents infinite loops)

10
BOOLEAN_TRUE_PATTERN =

Stricter boolean matching pattern - anchored to avoid partial matches

/\A(on|true|yes)\z/i.freeze
@@reporter_thread =
nil
@@running =
false
@@report_interval =

Hard-coded 1 second for real-time reporting

1
@@inline_display =

Lazy-loaded from DB, defaults to true

nil
@@mutex =

Thread safety for start/stop operations

Mutex.new

Class Method Summary collapse

Class Method Details

.active?Boolean

Checks if the experience gain reporter is currently running.

Returns:

  • (Boolean)

    true if the reporter is active, false otherwise.



103
104
105
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 103

def self.active?
  @@running
end

.format_briefexp_off(line, skill, rate_word) ⇒ String

Formats the BRIEFEXP OFF line with gained experience.

Parameters:

  • line (String)

    the line to format.

  • skill (String)

    the skill for which experience is gained.

  • rate_word (String)

    the word representing the rate of experience.

Returns:

  • (String)

    the formatted line.



177
178
179
180
181
182
183
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 177

def self.format_briefexp_off(line, skill, rate_word)
  return line unless @@inline_display

  gained = DRSkill.gained_exp(skill) || 0.00
  padded_rate = rate_word.ljust(DR_LONGEST_LEARNING_RATE_LENGTH)
  line.sub(/(%\s+)(#{Regexp.escape(rate_word)})/, "\\1#{padded_rate} #{format('%0.2f', gained)}")
end

.format_briefexp_on(line, skill) ⇒ String

Formats the BRIEFEXP ON line with gained experience.

Parameters:

  • line (String)

    the line to format.

  • skill (String)

    the skill for which experience is gained.

Returns:

  • (String)

    the formatted line.



165
166
167
168
169
170
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 165

def self.format_briefexp_on(line, skill)
  return line unless @@inline_display

  gained = DRSkill.gained_exp(skill) || 0.00
  line.sub(%r{(/34\])}, "\\1 #{format('%0.2f', gained)}")
end

.format_gains(gains_array) ⇒ Array<String>

Formats an array of skill gains into a readable string.

Parameters:

  • gains_array (Array<Hash>)

    an array of hashes containing skill gain information.

Returns:

  • (Array<String>)

    an array of formatted skill gain strings.



188
189
190
191
192
193
194
195
196
197
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 188

def self.format_gains(gains_array)
  # Aggregate multiple pulses of same skill
  aggregated = gains_array.reduce(Hash.new(0)) do |result, gain|
    result[gain[:skill]] += gain[:change]
    result
  end

  # Format as "Skill(+N)" strings
  aggregated.keys.sort.map { |skill| "#{skill}(+#{aggregated[skill]})" }
end

.inline_display=(value) ⇒ void

This method returns an undefined value.

Enables or disables inline experience display.

Parameters:

  • value (Boolean)

    true to enable, false to disable.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 145

def self.inline_display=(value)
  @@inline_display = BOOLEAN_TRUE_PATTERN.match?(value.to_s)
  retries = 0
  begin
    Lich.db.execute("INSERT OR REPLACE INTO lich_settings(name,value) values('display_inline_exp',?);", [@@inline_display.to_s.encode('UTF-8')])
  rescue SQLite3::BusyException
    retries += 1
    if retries >= MAX_SQLITE_RETRIES
      Lich::Messaging.msg('error', "DRExpMonitor: SQLite busy after #{MAX_SQLITE_RETRIES} retries, inline_display setting may not be persisted")
      return
    end
    sleep 0.1
    retry
  end
end

.inline_display?Boolean

Checks if inline experience display is enabled.

Returns:

  • (Boolean)

    true if inline display is enabled, false otherwise.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 119

def self.inline_display?
  if @@inline_display.nil?
    retries = 0
    begin
      val = Lich.db.get_first_value("SELECT value FROM lich_settings WHERE name='display_inline_exp';")
    rescue SQLite3::BusyException
      retries += 1
      if retries >= MAX_SQLITE_RETRIES
        Lich::Messaging.msg('error', "DRExpMonitor: SQLite busy after #{MAX_SQLITE_RETRIES} retries, defaulting inline_display to false")
        @@inline_display = false
        return @@inline_display
      end
      sleep 0.1
      retry
    end
    # Default to false - inline display must be explicitly enabled
    # Once enabled, the persisted value takes precedence
    # BUG FIX: Use anchored pattern to avoid matching partial strings like "money" or "trust"
    @@inline_display = val.nil? ? false : BOOLEAN_TRUE_PATTERN.match?(val.to_s)
  end
  @@inline_display
end

.report_skill_gainsvoid

This method returns an undefined value.

Reports the skill gains to the messaging system.



201
202
203
204
205
206
207
208
209
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 201

def self.report_skill_gains
  # Drain the gained_skills array
  new_skills = DRSkill.gained_skills.shift(DRSkill.gained_skills.size)
  return if new_skills.empty?

  # Format and display
  formatted_gains = format_gains(new_skills)
  Lich::Messaging.msg('info', "DRExpMonitor: #{formatted_gains.join(', ')}")
end

.reset!void

This method returns an undefined value.

Resets the state of the experience monitor.



109
110
111
112
113
114
115
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 109

def self.reset!
  @@mutex.synchronize do
    @@inline_display = nil
    @@running = false
    @@reporter_thread = nil
  end
end

.startvoid

Note:

This method will not start if the exp-monitor.lic script is running.

This method returns an undefined value.

Starts the background experience gain reporter.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 48

def self.start
  @@mutex.synchronize do
    if @@running
      Lich::Messaging.msg('info', 'DRExpMonitor: Experience gain reporting is already active')
      return
    end

    # Check for exp-monitor.lic conflict
    if Script.running?('exp-monitor')
      Lich::Messaging.msg('error', 'DRExpMonitor: Cannot start: exp-monitor.lic script is running')
      Lich::Messaging.msg('error', "DRExpMonitor: Stop it first with: #{$clean_lich_char}kill exp-monitor")
      return
    end

    @@running = true

    @@reporter_thread = Thread.new do
      begin
        loop do
          break unless @@running

          begin
            report_skill_gains
            sleep @@report_interval
          rescue StandardError => e
            Lich::Messaging.msg('error', "DRExpMonitor: Error: #{e.message}") if $DREXPMONITOR_DEBUG
            Lich.log "DRExpMonitor error: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
            sleep @@report_interval
          end
        end
      ensure
        @@running = false
      end
    end
  end
end

.stopvoid

Note:

This method will do nothing if the reporter is already inactive.

This method returns an undefined value.

Stops the background experience gain reporter.



88
89
90
91
92
93
94
95
96
97
98
99
# File 'documented/dragonrealms/drinfomon/drexpmonitor.rb', line 88

def self.stop
  @@mutex.synchronize do
    unless @@running
      Lich::Messaging.msg('info', 'DRExpMonitor: Experience gain reporting is already inactive')
      return
    end

    @@running = false
    @@reporter_thread&.kill
    @@reporter_thread = nil
  end
end