steam-condenser 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|