Module: Lich::Gemstone::Infomon::XMLParser

Defined in:
documented/gemstone/infomon/xmlparser.rb

Overview

this module handles all of the logic for parsing game lines that infomon depends on

Defined Under Namespace

Modules: Pattern

Class Method Summary collapse

Class Method Details

.parse(line) ⇒ Symbol

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Rescues StandardError and logs to Lich.log with full backtrace; always returns a symbol even on error (does not re-raise)

Parses a single line of XML game output and updates game state accordingly.

Detects NPC deaths, group arrivals, stow container configurations, ready item assignments, and status prompt markers. Uses a fast-path optimization: for single-line output, scans anchored patterns only at line start; for multiline buffered strings (containing interior newlines), falls back to full union to catch anchored patterns like death messages on inner lines.

Parameters:

  • line (String)

    a line of XML output from the game server

Returns:

  • (Symbol)

    :ok if the line matched and was processed, :noop if no pattern matched (safe to forward to other handlers)



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'documented/gemstone/infomon/xmlparser.rb', line 552

def self.parse(line)
  # Fast path: attempt the anchored union only at line start and scan just
  # the mid-line patterns. A non-terminal newline means a combined
  # multi-line string (e.g. buffered combat); fall back to the full union
  # so inner-line ^ anchors (e.g. death messages) still match.
  nl = line.index("\n")
  matched = if nl && nl < line.length - 1
              Pattern::All.match?(line)
            else
              Pattern::AllStart.match?(line) || Pattern::AllMidline.match?(line)
            end
  return :noop unless matched

  begin
    case line
    # this detects for death messages in XML that are not matched with appropriate combat attributes above
    when Pattern::NpcDeathMessage
      match = Regexp.last_match
      if (npc = GameObj.npcs.find { |obj| obj.id == match[:npc_id] && obj.status !~ /\b(?:dead|gone)\b/ })
        npc.status = 'dead'
      end
      :ok
    when Pattern::Group_Short
      return :noop unless (match_data = Group::Observer.wants?(line))
      Group::Observer.consume(line.strip, match_data)
      :ok
    when Pattern::Overwatch_Short
      return :noop unless (match_data = Overwatch::Observer.wants?(line))
      Overwatch::Observer.consume(line, match_data)
      :ok
    when Pattern::Also_Here_Arrival
      return :noop unless Lich::Claim::Lock.locked?
      line.scan(%r{<a exist=(?:'|")(?<id>.*?)(?:'|") noun=(?:'|")(?<noun>.*?)(?:'|")>(?<name>.*?)</a>}).each { |player_found|
        next unless player_found[0].to_s.start_with?('-')
        next if XMLData.arrival_pcs.include?(player_found[1])

        XMLData.arrival_pcs.push(player_found[1])
      }
      :ok
    when Pattern::StowListOutputStart
      StowList.reset
      :ok
    when Pattern::StowListContainer, Pattern::StowSetContainer1, Pattern::StowSetContainer2
      match = Regexp.last_match
      StowList.__send__("#{match[:type].downcase}=", GameObj.index_or_create(match[:id], match[:noun], match[:name], (match[:before].nil? ? nil : match[:before].strip), (match[:after].nil? ? nil : match[:after].strip)))
      StowList.checked = true if line =~ Pattern::StowListContainer
      :ok
    when Pattern::ReadyListOutputStart
      ReadyList.reset
      :ok
    when Pattern::ReadyListNormal, Pattern::ReadyListAmmo2, Pattern::ReadyListSheathsSet, Pattern::ReadyItemSet
      match = Regexp.last_match
      unless match[:id].nil?
        ReadyList.__send__("#{Lich::Util.normalize_name(match[:type].downcase)}=", GameObj.index_or_create(match[:id], match[:noun], match[:name], (match[:before].nil? ? nil : match[:before].strip), (match[:after].nil? ? nil : match[:after].strip)))
      end
      if match.named_captures.include?("store")
        ReadyList.__send__("store_#{Lich::Util.normalize_name(match[:type].downcase)}=", match[:store])
      end
      :ok
    when Pattern::ReadyListFinished
      ReadyList.checked = true
      :ok
    when Pattern::ReadyItemClear
      match = Regexp.last_match
      ReadyList.__send__("#{Lich::Util.normalize_name(match[:type].downcase)}=", nil)
      :ok
    when Pattern::ReadyStoreSet
      match = Regexp.last_match
      ReadyList.__send__("store_#{Lich::Util.normalize_name(match[:type].downcase)}=", match[:store])
      :ok
    when Pattern::StatusPrompt
      Infomon::Parser::State.set(Infomon::Parser::State::Ready) unless Infomon::Parser::State.get.eql?(Infomon::Parser::State::Ready)
      :ok
    else
      :noop
    end
  rescue StandardError
    respond "--- Lich: error: Infomon::XMLParser.parse: #{$!}"
    respond "--- Lich: error: line: #{line}"
    Lich.log "error: Infomon::XMLParser.parse: #{$!}\n\t#{$!.backtrace.join("\n\t")}"
    Lich.log "error: line: #{line}\n\t"
  end
end