steam-condenser 0.13.1 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/exceptions/packet_format_exception.rb +12 -5
- data/lib/exceptions/rcon_ban_exception.rb +11 -3
- data/lib/exceptions/rcon_no_auth_exception.rb +10 -3
- data/lib/exceptions/steam_condenser_exception.rb +8 -5
- data/lib/exceptions/timeout_exception.rb +13 -3
- data/lib/exceptions/web_api_exception.rb +31 -23
- data/lib/steam-condenser.rb +9 -3
- data/lib/steam-condenser/community.rb +8 -1
- data/lib/steam-condenser/servers.rb +7 -1
- data/lib/steam-condenser/version.rb +2 -1
- data/lib/steam/community/cacheable.rb +4 -4
- data/lib/steam/community/game_inventory.rb +119 -0
- data/lib/steam/community/game_item.rb +38 -0
- data/lib/steam/community/game_stats.rb +10 -4
- data/lib/steam/community/portal2/portal2_inventory.rb +21 -0
- data/lib/steam/community/portal2/portal2_item.rb +35 -0
- data/lib/steam/community/portal2/portal2_stats.rb +28 -0
- data/lib/steam/community/tf2/tf2_inventory.rb +7 -37
- data/lib/steam/community/tf2/tf2_item.rb +8 -79
- data/lib/steam/community/tf2/tf2_stats.rb +10 -13
- data/lib/steam/community/web_api.rb +1 -0
- data/lib/steam/packets/a2m_get_servers_batch2_packet.rb +37 -4
- data/lib/steam/packets/a2s_info_packet.rb +10 -5
- data/lib/steam/packets/a2s_player_packet.rb +16 -6
- data/lib/steam/packets/a2s_rules_packet.rb +16 -4
- data/lib/steam/packets/a2s_serverquery_getchallenge_packet.rb +11 -5
- data/lib/steam/packets/c2m_checkmd5_packet.rb +10 -4
- data/lib/steam/packets/m2a_server_batch_packet.rb +19 -3
- data/lib/steam/packets/m2c_isvalidmd5_packet.rb +14 -5
- data/lib/steam/packets/m2s_requestrestart_packet.rb +14 -4
- data/lib/steam/packets/rcon/rcon_auth_request.rb +15 -3
- data/lib/steam/packets/rcon/rcon_auth_response.rb +17 -3
- data/lib/steam/packets/rcon/rcon_exec_request.rb +15 -3
- data/lib/steam/packets/rcon/rcon_exec_response.rb +20 -3
- data/lib/steam/packets/rcon/rcon_goldsrc_request.rb +18 -3
- data/lib/steam/packets/rcon/rcon_goldsrc_response.rb +17 -3
- data/lib/steam/packets/rcon/rcon_packet.rb +31 -3
- data/lib/steam/packets/rcon/rcon_packet_factory.rb +15 -5
- data/lib/steam/packets/rcon/rcon_terminator.rb +14 -5
- data/lib/steam/packets/request_with_challenge.rb +10 -5
- data/lib/steam/packets/s2a_info2_packet.rb +14 -5
- data/lib/steam/packets/s2a_info_base_packet.rb +16 -6
- data/lib/steam/packets/s2a_info_detailed_packet.rb +15 -8
- data/lib/steam/packets/s2a_logstring_packet.rb +11 -5
- data/lib/steam/packets/s2a_player_packet.rb +14 -6
- data/lib/steam/packets/s2a_rules_packet.rb +15 -5
- data/lib/steam/packets/s2c_challenge_packet.rb +17 -6
- data/lib/steam/packets/s2m_heartbeat2_packet.rb +18 -4
- data/lib/steam/packets/steam_packet.rb +14 -5
- data/lib/steam/packets/steam_packet_factory.rb +25 -2
- data/lib/steam/servers/game_server.rb +154 -25
- data/lib/steam/servers/goldsrc_server.rb +35 -3
- data/lib/steam/servers/master_server.rb +77 -23
- data/lib/steam/servers/server.rb +42 -3
- data/lib/steam/servers/source_server.rb +35 -11
- data/lib/stringio_additions.rb +48 -3
- data/test/query_tests.rb +4 -4
- metadata +11 -6
@@ -5,16 +5,25 @@
|
|
5
5
|
|
6
6
|
require 'steam/packets/rcon/rcon_packet'
|
7
7
|
|
8
|
-
# This class
|
9
|
-
#
|
10
|
-
#
|
8
|
+
# This packet class represents a special SERVERDATA_RESPONSE_VALUE packet which
|
9
|
+
# is sent to the server
|
10
|
+
#
|
11
|
+
# It is used to determine the end of a RCON response from Source servers.
|
12
|
+
# Packets of this type are sent after the actual RCON command and the empty
|
13
|
+
# response packet from the server will indicate the end of the response.
|
14
|
+
#
|
15
|
+
# @author Sebastian Staudt
|
16
|
+
# @see SourceServer#rcon_exec
|
11
17
|
class RCONTerminator
|
12
18
|
|
13
19
|
include RCONPacket
|
14
20
|
|
15
|
-
# Creates a new
|
21
|
+
# Creates a new RCON terminator packet instance for the given request ID
|
22
|
+
#
|
23
|
+
# @param [Fixnum] request_id The request ID for the current RCON
|
24
|
+
# communication
|
16
25
|
def initialize(request_id)
|
17
|
-
super request_id,
|
26
|
+
super request_id, SERVERDATA_RESPONSE_VALUE, nil
|
18
27
|
end
|
19
28
|
|
20
29
|
end
|
@@ -1,12 +1,17 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
|
-
# This module implements a
|
6
|
+
# This module implements a method to generate raw packet data used by request
|
7
|
+
# packets which send a challenge number
|
8
|
+
#
|
9
|
+
# @author Sebastian Staudt
|
7
10
|
module RequestWithChallenge
|
8
11
|
|
9
|
-
# Returns
|
12
|
+
# Returns the raw data representing this packet
|
13
|
+
#
|
14
|
+
# @return string A string containing the raw data of this request packet
|
10
15
|
def to_s
|
11
16
|
[0xFF, 0xFF, 0xFF, 0xFF, @header_data, @content_data.string.to_i].pack('c5l')
|
12
17
|
end
|
@@ -1,17 +1,26 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/s2a_info_base_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This class represents a S2A_INFO_DETAILED response packet sent by a Source or
|
9
|
+
# GoldSrc server
|
10
|
+
#
|
11
|
+
# Out-of-date (before 10/24/2008) GoldSrc servers use an older format (see
|
12
|
+
# {S2A_INFO_DETAILED_Packet}).
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see GameServer#update_server_info
|
10
16
|
class S2A_INFO2_Packet
|
11
17
|
|
12
18
|
include S2A_INFO_BasePacket
|
13
19
|
|
14
|
-
# Creates a S2A_INFO2 response object based on the data
|
20
|
+
# Creates a new S2A_INFO2 response object based on the given data
|
21
|
+
#
|
22
|
+
# @param [String] data The raw packet data replied from the server
|
23
|
+
# @see S2A_INFO_BasePacket#generate_info_hash
|
15
24
|
def initialize(data)
|
16
25
|
super S2A_INFO2_HEADER, data
|
17
26
|
|
@@ -1,19 +1,29 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This module implements methods to generate and access server information from
|
9
|
+
# S2A_INFO_DETAILED and S2A_INFO2 response packets
|
10
|
+
#
|
11
|
+
# @author Sebastian Staudt
|
12
|
+
# @see S2A_INFO_DETAILED_Packet
|
13
|
+
# @see S2A_INFO2_Packet
|
10
14
|
module S2A_INFO_BasePacket
|
11
15
|
|
12
16
|
include SteamPacket
|
13
17
|
|
18
|
+
# Returns the information provided by the server
|
19
|
+
#
|
20
|
+
# @return [Hash<String, Object>] The information provided by the server
|
14
21
|
attr_reader :info_hash
|
15
22
|
|
16
|
-
|
23
|
+
protected
|
24
|
+
|
25
|
+
# Generates a hash of server properties from the instance variables of the
|
26
|
+
# including packet object
|
17
27
|
def generate_info_hash
|
18
28
|
@info_hash = Hash[
|
19
29
|
*instance_variables.map { |var|
|
@@ -1,19 +1,26 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2011 Sebastian Staudt
|
5
5
|
|
6
6
|
require "steam/packets/s2a_info_base_packet"
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
8
|
+
# This class represents a S2A_INFO_DETAILED response packet sent by a GoldSrc
|
9
|
+
# server
|
10
|
+
#
|
11
|
+
# This is deprecated by 10/24/2008. GoldSrc servers use the same format as
|
12
|
+
# Source servers now (see {S2A_INFO2_Packet}).
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see GameServer#update_server_info
|
12
16
|
class S2A_INFO_DETAILED_Packet
|
13
17
|
|
14
18
|
include S2A_INFO_BasePacket
|
15
19
|
|
16
|
-
# Creates a S2A_INFO_DETAILED response object based on the data
|
20
|
+
# Creates a new S2A_INFO_DETAILED response object based on the given data
|
21
|
+
#
|
22
|
+
# @param [String] data The raw packet data replied from the server
|
23
|
+
# @see S2A_INFO_BasePacket#generate_info_hash
|
17
24
|
def initialize(data)
|
18
25
|
super S2A_INFO_DETAILED_HEADER, data
|
19
26
|
|
@@ -1,19 +1,25 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
8
|
+
# This class represents a S2A_LOGSTRING packet used to transfer log messages
|
9
|
+
#
|
10
|
+
# @author Sebastian Staudt
|
9
11
|
class S2A_LOGSTRING_Packet
|
10
12
|
|
11
13
|
include SteamPacket
|
12
14
|
|
13
|
-
#
|
15
|
+
# Returns the log message contained in this packet
|
16
|
+
#
|
17
|
+
# @return [String] The log message
|
14
18
|
attr_reader :message
|
15
19
|
|
16
|
-
# Creates a new
|
20
|
+
# Creates a new S2A_LOGSTRING object based on the given data
|
21
|
+
#
|
22
|
+
# @param [String] data The raw packet data sent by the server
|
17
23
|
def initialize(data)
|
18
24
|
super S2A_LOGSTRING_HEADER, data
|
19
25
|
|
@@ -1,25 +1,33 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This class represents a S2A_PLAYER response sent by a game server
|
9
|
+
#
|
10
|
+
# It is used to transfer a list of players currently playing on the server.
|
11
|
+
#
|
12
|
+
# @author Sebastian Staudt
|
13
|
+
# @see GameServer#update_player_info
|
10
14
|
class S2A_PLAYER_Packet
|
11
15
|
|
12
16
|
include SteamPacket
|
13
17
|
|
18
|
+
# Returns the list of active players provided by the server
|
19
|
+
#
|
20
|
+
# @return [Hash<String, SteamPlayer>] All active players on the server
|
14
21
|
attr_reader :player_hash
|
15
22
|
|
16
|
-
# Creates a S2A_PLAYER response object based on the data
|
23
|
+
# Creates a new S2A_PLAYER response object based on the given data
|
24
|
+
#
|
25
|
+
# @param [String] content_data The raw packet data sent by the server
|
17
26
|
def initialize(content_data)
|
18
27
|
raise Exception.new('Wrong formatted S2A_PLAYER packet.') if content_data.nil?
|
19
28
|
|
20
29
|
super S2A_PLAYER_HEADER, content_data
|
21
30
|
|
22
|
-
# Ignore player count
|
23
31
|
@content_data.byte
|
24
32
|
@player_hash = {}
|
25
33
|
|
@@ -1,19 +1,29 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This class represents a S2A_RULES response sent by a game server
|
9
|
+
#
|
10
|
+
# It is used to transfer a list of server rules (a.k.a. CVARs) with their
|
11
|
+
# active values.
|
12
|
+
#
|
13
|
+
# @author Sebastian Staudt
|
14
|
+
# @see GameServer#update_rules_info
|
10
15
|
class S2A_RULES_Packet
|
11
16
|
|
12
17
|
include SteamPacket
|
13
18
|
|
19
|
+
# Returns the list of server rules (a.k.a. CVars) with the current values
|
20
|
+
#
|
21
|
+
# @return [Hash<String, String>] A list of server rules
|
14
22
|
attr_reader :rules_hash
|
15
23
|
|
16
|
-
# Creates a S2A_RULES response object based on the data
|
24
|
+
# Creates a new S2A_RULES response object based on the given data
|
25
|
+
#
|
26
|
+
# @param [String] content_data The raw packet data sent by the server
|
17
27
|
def initialize(content_data)
|
18
28
|
raise Exception.new('Wrong formatted S2A_RULES response packet.') if content_data.nil?
|
19
29
|
|
@@ -1,22 +1,33 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This packet class represents a S2C_CHALLENGE response replied by a game
|
9
|
+
# server
|
10
|
+
#
|
11
|
+
# It is used to provide a challenge number to a client requesting information
|
12
|
+
# from the game server.
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see GameServer#update_challenge_number
|
10
16
|
class S2C_CHALLENGE_Packet
|
11
17
|
|
12
18
|
include SteamPacket
|
13
19
|
|
14
|
-
# Creates a S2C_CHALLENGE response object based on the data
|
20
|
+
# Creates a new S2C_CHALLENGE response object based on the given data
|
21
|
+
#
|
22
|
+
# @param [String] challenge_number The raw packet data replied from the
|
23
|
+
# server
|
15
24
|
def initialize(challenge_number)
|
16
25
|
super S2C_CHALLENGE_HEADER, challenge_number
|
17
26
|
end
|
18
27
|
|
19
|
-
# Returns the challenge number received from the server
|
28
|
+
# Returns the challenge number received from the game server
|
29
|
+
#
|
30
|
+
# @return [Fixnum] The challenge number provided by the game server
|
20
31
|
def challenge_number
|
21
32
|
@content_data.rewind
|
22
33
|
@content_data.long
|
@@ -5,12 +5,19 @@
|
|
5
5
|
|
6
6
|
require 'steam/packets/steam_packet'
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# This class represents a S2M_HEARTBEAT2 packet sent by game servers to master
|
9
|
+
# servers
|
10
|
+
#
|
11
|
+
# It is used to signal the game server's availability and status to the master
|
12
|
+
# servers.
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see MasterServer#send_heartbeat
|
10
16
|
class S2M_HEARTBEAT2_Packet
|
11
17
|
|
12
18
|
include SteamPacket
|
13
19
|
|
20
|
+
# Default data to send with a S2M_HEARTBEAT2 packet
|
14
21
|
DEFAULT_DATA = {
|
15
22
|
:appid => 320,
|
16
23
|
:bots => 0,
|
@@ -34,7 +41,12 @@ class S2M_HEARTBEAT2_Packet
|
|
34
41
|
:version => '1.0.0.0'
|
35
42
|
}
|
36
43
|
|
37
|
-
# Creates a new
|
44
|
+
# Creates a new S2M_HEARTBEAT2 packet object based on the given data
|
45
|
+
#
|
46
|
+
# @param [Hash<Symbol, Object>] data The data to send with the heartbeat. The
|
47
|
+
# data contents are merge with the values from {DEFAULT_DATA}.
|
48
|
+
# @raise [SteamCondenserException] when the required challenge number is
|
49
|
+
# missing
|
38
50
|
def initialize(data = {})
|
39
51
|
data = DEFAULT_DATA.merge data
|
40
52
|
|
@@ -49,7 +61,9 @@ class S2M_HEARTBEAT2_Packet
|
|
49
61
|
super S2M_HEARTBEAT2_HEADER, bytes
|
50
62
|
end
|
51
63
|
|
52
|
-
# Returns
|
64
|
+
# Returns the raw data representing this packet
|
65
|
+
#
|
66
|
+
# @return [String] A string containing the raw data of this request packet
|
53
67
|
def to_s
|
54
68
|
[@header_data, @content_data.string].pack('ca*')
|
55
69
|
end
|
@@ -1,11 +1,15 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
require 'stringio_additions'
|
7
7
|
|
8
|
-
# This
|
8
|
+
# This module implements the basic functionality used by most of the packets
|
9
|
+
# used in communication with master, Source or GoldSrc servers.
|
10
|
+
#
|
11
|
+
# @author Sebastian Staudt
|
12
|
+
# @see SteamPacketFactory
|
9
13
|
module SteamPacket
|
10
14
|
|
11
15
|
A2M_GET_SERVERS_BATCH2_HEADER = 0x31
|
@@ -29,13 +33,18 @@ module SteamPacket
|
|
29
33
|
S2C_CHALLENGE_HEADER = 0x41
|
30
34
|
S2M_HEARTBEAT2_HEADER = 0x30
|
31
35
|
|
32
|
-
# Creates a new
|
36
|
+
# Creates a new packet object based on the given data
|
37
|
+
#
|
38
|
+
# @param [Fixnum] header_data The packet header
|
39
|
+
# @param [String] content_data The raw data of the packet
|
33
40
|
def initialize(header_data, content_data = '')
|
34
41
|
@content_data = StringIO.new content_data.to_s
|
35
42
|
@header_data = header_data
|
36
43
|
end
|
37
44
|
|
38
|
-
# Returns
|
45
|
+
# Returns the raw data representing this packet
|
46
|
+
#
|
47
|
+
# @return [String] A string containing the raw data of this request packet
|
39
48
|
def to_s
|
40
49
|
[0xFF, 0xFF, 0xFF, 0xFF, @header_data, @content_data.string].pack('c5a*')
|
41
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
@@ -20,9 +20,19 @@ require 'steam/packets/m2s_requestrestart_packet'
|
|
20
20
|
require 'steam/packets/s2a_logstring_packet'
|
21
21
|
require 'steam/packets/rcon/rcon_goldsrc_response'
|
22
22
|
|
23
|
+
# This module provides functionality to handle raw packet data, including
|
24
|
+
# data split into several UDP / TCP packets and BZIP2 compressed data. It's the
|
25
|
+
# main utility to transform data bytes into packet objects.
|
26
|
+
#
|
27
|
+
# @author Sebastian Staudt
|
28
|
+
# @see SteamPacket
|
23
29
|
module SteamPacketFactory
|
24
30
|
|
25
31
|
# Creates a new packet object based on the header byte of the given raw data
|
32
|
+
#
|
33
|
+
# @param [String] raw_data The raw data of the packet
|
34
|
+
# @raise [SteamCondenserException] if the packet header is not recognized
|
35
|
+
# @return [SteamPacket] The packet object generated from the packet data
|
26
36
|
def self.packet_from_data(raw_data)
|
27
37
|
header = raw_data[0].ord
|
28
38
|
data = raw_data[1..-1]
|
@@ -65,6 +75,19 @@ module SteamPacketFactory
|
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
# Reassembles the data of a split and/or compressed packet into a single
|
79
|
+
# packet object
|
80
|
+
#
|
81
|
+
# @param [Array<String>] split_packets An array of packet data
|
82
|
+
# @param [true, false] is_compressed whether the data of this packet is
|
83
|
+
# compressed
|
84
|
+
# @param [Fixnum] packet_checksum The CRC32 checksum of the decompressed
|
85
|
+
# packet data
|
86
|
+
# @raise [SteamCondenserException] if the bz2 gem is not installed
|
87
|
+
# @raise [PacketFormatException] if the calculated CRC32 checksum does not
|
88
|
+
# match the expected value
|
89
|
+
# @return [SteamPacket] The reassembled packet
|
90
|
+
# @see packet_from_data
|
68
91
|
def self.reassemble_packet(split_packets, is_compressed = false, packet_checksum = 0)
|
69
92
|
packet_data = split_packets.join ''
|
70
93
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# This code is free software; you can redistribute it and/or modify it under
|
2
|
-
# terms of the new BSD License.
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
3
|
#
|
4
4
|
# Copyright (c) 2008-2011, Sebastian Staudt
|
5
5
|
|
@@ -15,6 +15,11 @@ require 'steam/packets/s2a_rules_packet'
|
|
15
15
|
require 'steam/packets/s2c_challenge_packet'
|
16
16
|
require 'steam/servers/server'
|
17
17
|
|
18
|
+
# This module is included by classes representing different game server
|
19
|
+
# implementations and provides the basic functionality to communicate with
|
20
|
+
# them using the common query protocol
|
21
|
+
#
|
22
|
+
# @author Sebastian Staudt
|
18
23
|
module GameServer
|
19
24
|
|
20
25
|
include Server
|
@@ -24,7 +29,11 @@ module GameServer
|
|
24
29
|
REQUEST_PLAYER = 2
|
25
30
|
REQUEST_RULES = 3
|
26
31
|
|
27
|
-
# Parses the player attribute names supplied by
|
32
|
+
# Parses the player attribute names supplied by `rcon status`
|
33
|
+
#
|
34
|
+
# @param [String] status_header The header line provided by `rcon status`
|
35
|
+
# @return [Array<Symbol>] Split player attribute names
|
36
|
+
# @see .split_player_status
|
28
37
|
def self.player_status_attributes(status_header)
|
29
38
|
status_header.split.map do |attribute|
|
30
39
|
case attribute
|
@@ -38,7 +47,13 @@ module GameServer
|
|
38
47
|
end
|
39
48
|
end
|
40
49
|
|
41
|
-
# Splits the player status obtained with
|
50
|
+
# Splits the player status obtained with `rcon status`
|
51
|
+
#
|
52
|
+
# @param [Array<Symbol>] attributes The attribute names
|
53
|
+
# @param [String] player_status The status line of a single player
|
54
|
+
# @return [Hash<Symbol, String>] The attributes with the corresponding values
|
55
|
+
# for this player
|
56
|
+
# @see .player_status_attributes
|
42
57
|
def self.split_player_status(attributes, player_status)
|
43
58
|
player_status.sub! /^\d+ +/, '' if attributes.first != :userid
|
44
59
|
|
@@ -68,66 +83,119 @@ module GameServer
|
|
68
83
|
|
69
84
|
# Creates a new instance of a game server object
|
70
85
|
#
|
71
|
-
#
|
86
|
+
# @param [String] address Either an IP address, a DNS name or one of them
|
87
|
+
# combined with the port number. If a port number is given, e.g.
|
88
|
+
# 'server.example.com:27016' it will override the second argument.
|
89
|
+
# @param [Fixnum] port The port the server is listening on
|
90
|
+
# @raise [SteamCondenserException] if an host name cannot be resolved
|
72
91
|
def initialize(address, port = 27015)
|
73
92
|
super
|
74
93
|
end
|
75
94
|
|
76
95
|
# Returns the last measured response time of this server
|
77
96
|
#
|
78
|
-
# If
|
79
|
-
#
|
97
|
+
# If the latency hasn't been measured yet, it is done when calling this
|
98
|
+
# method for the first time.
|
80
99
|
#
|
81
|
-
#
|
100
|
+
# If this information is vital to you, be sure to call {#update_ping}
|
101
|
+
# regularly to stay up-to-date.
|
102
|
+
#
|
103
|
+
# @return [Fixnum] The latency of this server in milliseconds
|
104
|
+
# @see #update_ping
|
82
105
|
def ping
|
83
106
|
update_ping if @ping.nil?
|
84
107
|
@ping
|
85
108
|
end
|
86
109
|
|
87
|
-
# Returns
|
110
|
+
# Returns a list of players currently playing on this server
|
88
111
|
#
|
89
|
-
# If
|
90
|
-
#
|
112
|
+
# If the players haven't been fetched yet, it is done when calling this
|
113
|
+
# method for the first time.
|
91
114
|
#
|
92
115
|
# As the players and their scores change quite often be sure to update this
|
93
|
-
# list regularly by calling
|
116
|
+
# list regularly by calling {#update_players} if you rely on this
|
117
|
+
# information.
|
118
|
+
#
|
119
|
+
# @param [String] rcon_password The RCON password of this server may be
|
120
|
+
# provided to gather more detailed information on the players, like
|
121
|
+
# STEAM_IDs.
|
122
|
+
# @return [Hash] The players on this server
|
123
|
+
# @see update_players
|
94
124
|
def players(rcon_password = nil)
|
95
|
-
|
125
|
+
update_players(rcon_password) if @player_hash.nil?
|
96
126
|
@player_hash
|
97
127
|
end
|
98
128
|
|
129
|
+
# Authenticates the connection for RCON communication with the server
|
130
|
+
#
|
131
|
+
# @abstract Must be be implemented by including classes to handle the
|
132
|
+
# authentication
|
133
|
+
# @param [String] password The RCON password of the server
|
134
|
+
# @return [Boolean] whether the authentication was successful
|
135
|
+
# @see #rcon_exec
|
136
|
+
def rcon_auth(password)
|
137
|
+
end
|
138
|
+
|
99
139
|
def rcon_authenticated?
|
100
140
|
@rcon_authenticated
|
101
141
|
end
|
102
142
|
|
103
|
-
#
|
104
|
-
# also called rules.
|
105
|
-
# The hash has the format of +rule_name+ => +rule_value+
|
143
|
+
# Remotely executes a command on the server via RCON
|
106
144
|
#
|
107
|
-
#
|
145
|
+
# @abstract Must be be implemented by including classes to handle the command
|
146
|
+
# execution
|
147
|
+
# @param [String] command The command to execute on the server
|
148
|
+
# @return [String] The output of the executed command
|
149
|
+
# @see #rcon_auth
|
150
|
+
def rcon_exec(command)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns the settings applied on the server. These settings are also called
|
108
154
|
# rules.
|
109
155
|
#
|
156
|
+
# If the rules haven't been fetched yet, it is done when calling this method
|
157
|
+
# for the first time.
|
158
|
+
#
|
110
159
|
# As the rules usually don't change often, there's almost no need to update
|
111
160
|
# this hash. But if you need to, you can achieve this by calling
|
112
|
-
#
|
161
|
+
# {#update_rules}.
|
162
|
+
#
|
163
|
+
# @return [Hash<String, String>] The currently active server rules
|
164
|
+
# @see #update_rules
|
113
165
|
def rules
|
114
|
-
|
166
|
+
update_rules if @rules_hash.nil?
|
115
167
|
@rules_hash
|
116
168
|
end
|
117
169
|
|
118
170
|
# Returns a hash with basic information on the server.
|
119
171
|
#
|
120
|
-
# If
|
121
|
-
#
|
172
|
+
# If the server information haven't been fetched yet, it is done when
|
173
|
+
# calling this method for the first time.
|
122
174
|
#
|
123
175
|
# The server information usually only changes on map change and when players
|
124
176
|
# join or leave. As the latter changes can be monitored by calling
|
125
|
-
#
|
177
|
+
# {#update_players}, there's no need to call {#update_server_info} very
|
178
|
+
# often.
|
179
|
+
#
|
180
|
+
# @return [Hash] Server attributes with their values
|
181
|
+
# @see #update_server_info
|
126
182
|
def server_info
|
127
183
|
update_server_info if @info_hash.nil?
|
128
184
|
@info_hash
|
129
185
|
end
|
130
186
|
|
187
|
+
# Sends the specified request to the server and handles the returned response
|
188
|
+
#
|
189
|
+
# Depending on the given request type this will fill the various data
|
190
|
+
# attributes of the server object.
|
191
|
+
#
|
192
|
+
# @param [Fixnum] request_type The type of request to send to the server
|
193
|
+
# @param [Boolean] repeat_on_failure Whether the request should be repeated,
|
194
|
+
# if the replied packet isn't expected. This is useful to handle
|
195
|
+
# missing challenge numbers, which will be automatically filled in,
|
196
|
+
# although not requested explicitly.
|
197
|
+
# @raise [SteamCondenserException] if either the request type or the response
|
198
|
+
# packet is not known
|
131
199
|
def handle_response_for_request(request_type, repeat_on_failure = true)
|
132
200
|
begin
|
133
201
|
case request_type
|
@@ -171,13 +239,30 @@ module GameServer
|
|
171
239
|
end
|
172
240
|
end
|
173
241
|
|
242
|
+
# Initializes this server object with basic information
|
243
|
+
#
|
244
|
+
# @see #update_challenge_number
|
245
|
+
# @see #update_ping
|
246
|
+
# @see #update_server_info
|
174
247
|
def init
|
175
248
|
update_ping
|
176
249
|
update_server_info
|
177
250
|
update_challenge_number
|
178
251
|
end
|
179
252
|
|
180
|
-
|
253
|
+
# Sends a A2S_PLAYERS request to the server and updates the players' data for
|
254
|
+
# this server
|
255
|
+
#
|
256
|
+
# As the players and their scores change quite often be sure to update this
|
257
|
+
# list regularly by calling this method if you rely on this
|
258
|
+
# information.
|
259
|
+
#
|
260
|
+
# @param [String] rcon_password The RCON password of this server may be
|
261
|
+
# provided to gather more detailed information on the players, like
|
262
|
+
# STEAM_IDs.
|
263
|
+
# @see #handle_response_for_request
|
264
|
+
# @see #players
|
265
|
+
def update_players(rcon_password = nil)
|
181
266
|
handle_response_for_request GameServer::REQUEST_PLAYER
|
182
267
|
|
183
268
|
unless rcon_password.nil? || @player_hash.nil? || @player_hash.empty?
|
@@ -198,18 +283,52 @@ module GameServer
|
|
198
283
|
end
|
199
284
|
end
|
200
285
|
|
201
|
-
|
286
|
+
# Sends a A2S_RULES request to the server and updates the rules of this
|
287
|
+
# server
|
288
|
+
#
|
289
|
+
# As the rules usually don't change often, there's almost no need to update
|
290
|
+
# this hash. But if you need to, you can achieve this by calling this method.
|
291
|
+
#
|
292
|
+
# @see #handle_response_for_request
|
293
|
+
# @see #rules
|
294
|
+
def update_rules
|
202
295
|
handle_response_for_request GameServer::REQUEST_RULES
|
203
296
|
end
|
204
297
|
|
298
|
+
# Sends a A2S_INFO request to the server and updates this server's basic
|
299
|
+
# information
|
300
|
+
#
|
301
|
+
# The server information usually only changes on map change and when players
|
302
|
+
# join or leave. As the latter changes can be monitored by calling
|
303
|
+
# {#update_players}, there's no need to call this method very often.
|
304
|
+
#
|
305
|
+
# @see #handle_response_for_request
|
306
|
+
# @see #server_info
|
205
307
|
def update_server_info
|
206
308
|
handle_response_for_request GameServer::REQUEST_INFO
|
207
309
|
end
|
208
310
|
|
311
|
+
# Sends a A2S_SERVERQUERY_GETCHALLENGE request to the server and updates the
|
312
|
+
# challenge number used to communicate with this server
|
313
|
+
#
|
314
|
+
# There's usually no need to call this method explicitly, because
|
315
|
+
# {#handle_response_for_request} will automatically get the challenge number
|
316
|
+
# when the server assigns a new one.
|
317
|
+
#
|
318
|
+
# @see #handle_response_for_request
|
319
|
+
# @see #init
|
209
320
|
def update_challenge_number
|
210
321
|
handle_response_for_request GameServer::REQUEST_CHALLENGE
|
211
322
|
end
|
212
323
|
|
324
|
+
# Sends a A2S_INFO request to the server and measures the time needed for the
|
325
|
+
# reply
|
326
|
+
#
|
327
|
+
# If this information is vital to you, be sure to call this method regularly
|
328
|
+
# to stay up-to-date.
|
329
|
+
#
|
330
|
+
# @return [Fixnum] The latency of this server in milliseconds
|
331
|
+
# @see #ping
|
213
332
|
def update_ping
|
214
333
|
send_request A2S_INFO_Packet.new
|
215
334
|
start_time = Time.now
|
@@ -218,6 +337,10 @@ module GameServer
|
|
218
337
|
@ping = (end_time - start_time) * 1000
|
219
338
|
end
|
220
339
|
|
340
|
+
# Returns a human-readable text representation of the server
|
341
|
+
#
|
342
|
+
# @return [String] Available information about the server in a human-readable
|
343
|
+
# format
|
221
344
|
def to_s
|
222
345
|
return_string = ''
|
223
346
|
|
@@ -250,11 +373,17 @@ module GameServer
|
|
250
373
|
|
251
374
|
protected
|
252
375
|
|
376
|
+
# Receives a response from the server
|
377
|
+
#
|
378
|
+
# @return [SteamPacket] The response packet replied by the server
|
253
379
|
def reply
|
254
380
|
@socket.reply
|
255
381
|
end
|
256
382
|
|
257
|
-
|
383
|
+
# Sends a request packet to the server
|
384
|
+
#
|
385
|
+
# @param [SteamPacket] packet The request packet to send to the server
|
386
|
+
def send_request(packet)
|
258
387
|
@socket.send packet
|
259
388
|
end
|
260
389
|
|