Module: RconCs::Utils::Parsers

Included in:
Servers::SourceServer
Defined in:
lib/rcon_cs/utils/parsers.rb

Overview

Utility parser methods

Instance Method Summary collapse

Instance Method Details

#parse_players(data) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rcon_cs/utils/parsers.rb', line 87

def parse_players(data)
  body = data[4..]

  raise "Invalid packet type: #{body[0]}" unless body[0] == "D"

  num_of_players = body[1].ord
  body = body[2..]

  result = []
  offset = 0

  num_of_players.times do |player_idx|
    # Ensure enough data remains
    raise "Insufficient data for player #{player_idx}" if offset >= body.length

    offset += 1

    name_end = body[offset..].index("\x00")
    raise "No null terminator for player #{player_idx} name" unless name_end

    name = body[offset...(offset + name_end)].force_encoding("UTF-8")
    offset += name_end + 1

    # Ensure enough bytes for score and duration (4 each)
    raise "Missing score/duration for player #{player_idx}" if offset + 8 > body.length

    # Score (4 bytes, little-endian long)
    score = body[offset...(offset + 4)].unpack1("l<")
    offset += 4

    # Duration (4 bytes, little-endian float)
    duration = body[offset...(offset + 4)].unpack1("e")
    offset += 4

    result << { name: name, score: score, duration: duration }
  end

  warn "Extra data after parsing: #{body[offset..].bytes}" if offset < body.length

  result
end

#parse_server_info(data) ⇒ Object



8
9
10
11
12
13
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
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
84
85
# File 'lib/rcon_cs/utils/parsers.rb', line 8

def parse_server_info(data)
  body = data[4..]

  result = {}
  result[:protocol] = body[1].ord

  server_name_end = body[2..].index("\x00") + 2
  result[:server_name] = body[2...server_name_end].force_encoding("UTF-8")
  body = body[server_name_end + 1..]

  map_name_end = body.index("\x00")
  result[:map_name] = body[0...map_name_end]
  body = body[map_name_end + 1..]

  game_folder_end = body.index("\x00")
  result[:game_folder] = body[0...game_folder_end]
  body = body[game_folder_end + 1..]

  game_name_end = body.index("\x00")
  result[:game_name] = body[0...game_name_end]
  body = body[game_name_end + 1..]

  result[:app_id] = body[0..1].unpack1("S<")
  body = body[2..]

  result[:players] = body[0].ord
  body = body[1..]

  result[:max_players] = body[0].ord
  body = body[1..]

  result[:bots] = body[0].ord
  body = body[1..]

  result[:server_type] = body[0]
  body = body[1..]

  result[:environment] = body[0]
  body = body[1..]

  result[:visibility] = body[0].ord
  body = body[1..]

  result[:vac] = body[0].ord
  body = body[1..]

  version_end = body.index("\x00")
  result[:version] = body[0...version_end]
  body = body[version_end + 1..]

  edf = body[0].ord
  body = body[1..]

  if edf & 0x80
    result[:port] = body[0..1].unpack1("S<")
    body = body[2..]
  end

  if edf & 0x10
    result[:steam_id] = body[0..7].unpack1("Q<")
    body = body[8..]
  end

  # SourceTV port and name (not present in this case)
  if edf & 0x40
    # Skip if present
  end

  if edf & 0x20
    keywords_end = body.index("\x00")
    result[:keywords] = body[0...keywords_end]
    body = body[keywords_end + 1..]
  end

  result[:game_id] = body[0..7].unpack1("Q<") if edf & 0x01

  result
end