Module: GameServer

Includes:
Server
Included in:
GoldSrcServer, SourceServer
Defined in:
lib/steam/servers/game_server.rb

Constant Summary collapse

REQUEST_CHALLENGE =
0
REQUEST_INFO =
1
REQUEST_PLAYER =
2
REQUEST_RULES =
3

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Server

#rotate_ip

Class Method Details

.player_status_attributes(status_header) ⇒ Object

Parses the player attribute names supplied by rcon status



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/steam/servers/game_server.rb', line 28

def self.player_status_attributes(status_header)
  status_header.split.map do |attribute|
    case attribute
      when 'connected'
        :time
      when 'frag'
        :score
      else
        attribute.to_sym
    end
  end
end

.split_player_status(attributes, player_status) ⇒ Object

Splits the player status obtained with rcon status



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
# File 'lib/steam/servers/game_server.rb', line 42

def self.split_player_status(attributes, player_status)
  player_status.sub! /^\d+ +/, '' if attributes.first != :userid

  first_quote = player_status.index '"'
  last_quote  = player_status.rindex '"'
  data = [
    player_status[0, first_quote],
    player_status[first_quote + 1..last_quote - 1],
    player_status[last_quote + 1..-1]
  ]
  data = [ data[0].split, data[1], data[2].split ]
  data.flatten!

  if attributes.size > data.size && attributes.include?(:state)
    data.insert 3, nil, nil, nil
  elsif attributes.size < data.size
    data.delete_at 1
  end

  player_data = {}
  data.each_index do |i|
    player_data[attributes[i]] = data[i]
  end

  player_data
end

Instance Method Details

#handle_response_for_request(request_type, repeat_on_failure = true) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/steam/servers/game_server.rb', line 131

def handle_response_for_request(request_type, repeat_on_failure = true)
  begin
    case request_type
      when GameServer::REQUEST_CHALLENGE then
        request_packet = A2S_SERVERQUERY_GETCHALLENGE_Packet.new
        expected_response = S2C_CHALLENGE_Packet
      when GameServer::REQUEST_INFO then
        request_packet = A2S_INFO_Packet.new
        expected_response = S2A_INFO_BasePacket
      when GameServer::REQUEST_PLAYER then
        request_packet = A2S_PLAYER_Packet.new(@challenge_number)
        expected_response = S2A_PLAYER_Packet
      when GameServer::REQUEST_RULES then
        request_packet = A2S_RULES_Packet.new(@challenge_number)
        expected_response = S2A_RULES_Packet
      else
        raise SteamCondenserException.new("Called with wrong request type.")
    end

    send_request request_packet
    response_packet = reply

    if response_packet.kind_of? S2A_INFO_BasePacket
      @info_hash = response_packet.info_hash
    elsif response_packet.kind_of? S2A_PLAYER_Packet
      @player_hash = response_packet.player_hash
    elsif response_packet.kind_of? S2A_RULES_Packet
      @rules_hash = response_packet.rules_hash
    elsif response_packet.kind_of? S2C_CHALLENGE_Packet
      @challenge_number = response_packet.challenge_number
    else
      raise SteamCondenserException.new("Response of type #{response_packet.class} cannot be handled by this method.")
    end

    unless response_packet.kind_of? expected_response
      puts "Expected #{expected_response}, got #{response_packet.class}." if $DEBUG
      handle_response_for_request(request_type, false) if repeat_on_failure
    end
  rescue TimeoutException
    puts "Expected #{expected_response}, but timed out." if $DEBUG
  end
end

#initObject



174
175
176
177
178
# File 'lib/steam/servers/game_server.rb', line 174

def init
  update_ping
  update_server_info
  update_challenge_number
end

#initialize(address, port = 27015) ⇒ Object

Creates a new instance of a game server object

The port defaults to 27015 for game servers



72
73
74
# File 'lib/steam/servers/game_server.rb', line 72

def initialize(address, port = 27015)
  super
end

#pingObject

Returns the last measured response time of this server

If there’s no data, update_ping is called to measure the current response time of the server.

Whenever you want to get a new value for the ping time call update_ping.



82
83
84
85
# File 'lib/steam/servers/game_server.rb', line 82

def ping
  update_ping if @ping.nil?
  @ping
end

#players(rcon_password = nil) ⇒ Object

Returns an array of the player’s currently playing on this server.

If there’s no data, update_player_info is called to get the current list of players.

As the players and their scores change quite often be sure to update this list regularly by calling update_player_info.



94
95
96
97
# File 'lib/steam/servers/game_server.rb', line 94

def players(rcon_password = nil)
  update_player_info(rcon_password) if @player_hash.nil?
  @player_hash
end

#rcon_authenticated?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/steam/servers/game_server.rb', line 99

def rcon_authenticated?
  @rcon_authenticated
end

#rulesObject

Returns a hash of the settings applied on the server. These settings are also called rules. The hash has the format of rule_name => rule_value

If there’s no data, update_rules_info is called to get the current list of rules.

As the rules usually don’t change often, there’s almost no need to update this hash. But if you need to, you can achieve this by calling update_rules_info.



113
114
115
116
# File 'lib/steam/servers/game_server.rb', line 113

def rules
  update_rules_info if @rules_hash.nil?
  @rules_hash
end

#server_infoObject

Returns a hash with basic information on the server.

If there’s no data, update_server_info is called to get up-to-date information.

The server information usually only changes on map change and when players join or leave. As the latter changes can be monitored by calling update_player_info, there’s no need to call update_server_info very often.



126
127
128
129
# File 'lib/steam/servers/game_server.rb', line 126

def server_info
  update_server_info if @info_hash.nil?
  @info_hash
end

#to_sObject



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/steam/servers/game_server.rb', line 221

def to_s
  return_string = ''

  return_string << "Ping: #{@ping}\n"
  return_string << "Challenge number: #{@challenge_number}\n"

  unless @info_hash.nil?
    return_string << "Info:\n"
    @info_hash.each do |key, value|
      return_string << "  #{key}: #{value.inspect}\n"
    end
  end

  unless @player_hash.nil?
    return_string << "Players:\n"
    @player_hash.each_value do |player|
      return_string << "  #{player}\n"
    end
  end

  unless @rules_hash.nil?
    return_string << "Rules:\n"
    @rules_hash.each do |key, value|
      return_string << "  #{key}: #{value}\n"
    end
  end

  return_string
end

#update_challenge_numberObject



209
210
211
# File 'lib/steam/servers/game_server.rb', line 209

def update_challenge_number
  handle_response_for_request GameServer::REQUEST_CHALLENGE
end

#update_pingObject



213
214
215
216
217
218
219
# File 'lib/steam/servers/game_server.rb', line 213

def update_ping
  send_request A2S_INFO_Packet.new
  start_time = Time.now
  reply
  end_time = Time.now
  @ping = (end_time - start_time) * 1000
end

#update_player_info(rcon_password = nil) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/steam/servers/game_server.rb', line 180

def update_player_info(rcon_password = nil)
  handle_response_for_request GameServer::REQUEST_PLAYER

  unless rcon_password.nil? || @player_hash.nil? || @player_hash.empty?
    rcon_auth rcon_password
    players = rcon_exec('status').lines.select do |line|
      line.start_with?('#') && line != "#end\n"
    end.map do |line|
      line[1..-1].strip
    end
    attributes = GameServer.player_status_attributes players.shift

    players.each do |player|
      player_data = GameServer.split_player_status(attributes, player)
      if @player_hash.key? player_data[:name]
        @player_hash[player_data[:name]].add_info player_data
      end
    end
  end
end

#update_rules_infoObject



201
202
203
# File 'lib/steam/servers/game_server.rb', line 201

def update_rules_info
  handle_response_for_request GameServer::REQUEST_RULES
end

#update_server_infoObject



205
206
207
# File 'lib/steam/servers/game_server.rb', line 205

def update_server_info
  handle_response_for_request GameServer::REQUEST_INFO
end