steam-condenser 0.10.1 → 0.11.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/VERSION.yml +2 -2
- data/lib/datagram_channel.rb +30 -33
- data/lib/exceptions/packet_format_exception.rb +7 -3
- data/lib/exceptions/rcon_ban_exception.rb +7 -5
- data/lib/exceptions/rcon_no_auth_exception.rb +8 -6
- data/lib/exceptions/timeout_exception.rb +10 -4
- data/lib/socket_channel.rb +11 -17
- data/lib/steam/community/cacheable.rb +5 -7
- data/lib/steam/community/css/css_map.rb +31 -0
- data/lib/steam/community/css/css_stats.rb +116 -0
- data/lib/steam/community/css/css_weapon.rb +33 -0
- data/lib/steam/community/dods/dods_stats.rb +3 -3
- data/lib/steam/community/game_stats.rb +14 -13
- data/lib/steam/community/game_weapon.rb +3 -5
- data/lib/steam/community/l4d/abstract_l4d_stats.rb +4 -2
- data/lib/steam/community/l4d/l4d_explosive.rb +2 -2
- data/lib/steam/community/l4d/l4d_map.rb +3 -3
- data/lib/steam/community/steam_group.rb +2 -4
- data/lib/steam/community/steam_id.rb +2 -2
- data/lib/steam/community/tf2/tf2_class.rb +17 -17
- data/lib/steam/community/tf2/tf2_class_factory.rb +17 -17
- data/lib/steam/community/tf2/tf2_engineer.rb +9 -9
- data/lib/steam/community/tf2/tf2_medic.rb +9 -9
- data/lib/steam/community/tf2/tf2_sniper.rb +7 -7
- data/lib/steam/community/tf2/tf2_spy.rb +9 -9
- data/lib/steam/community/tf2/tf2_stats.rb +9 -9
- data/lib/steam/packets/a2a_ack_packet.rb +11 -9
- data/lib/steam/packets/a2a_ping_packet.rb +5 -4
- data/lib/steam/packets/a2m_get_servers_batch2_packet.rb +13 -12
- data/lib/steam/packets/a2s_info_packet.rb +8 -7
- data/lib/steam/packets/a2s_player_packet.rb +6 -6
- data/lib/steam/packets/a2s_rules_packet.rb +5 -5
- data/lib/steam/packets/a2s_serverquery_getchallenge_packet.rb +4 -3
- data/lib/steam/packets/m2a_server_batch_packet.rb +20 -22
- data/lib/steam/packets/rcon/rcon_auth_request.rb +5 -5
- data/lib/steam/packets/rcon/rcon_auth_response.rb +6 -6
- data/lib/steam/packets/rcon/rcon_exec_request.rb +5 -5
- data/lib/steam/packets/rcon/rcon_exec_response.rb +2 -2
- data/lib/steam/packets/rcon/rcon_goldsrc_request.rb +7 -7
- data/lib/steam/packets/rcon/rcon_goldsrc_response.rb +8 -8
- data/lib/steam/packets/rcon/rcon_packet.rb +12 -16
- data/lib/steam/packets/rcon/rcon_packet_factory.rb +19 -19
- data/lib/steam/packets/request_with_challenge.rb +5 -5
- data/lib/steam/packets/s2a_info2_packet.rb +28 -30
- data/lib/steam/packets/s2a_info_base_packet.rb +11 -16
- data/lib/steam/packets/s2a_info_detailed_packet.rb +30 -28
- data/lib/steam/packets/s2a_player_packet.rb +7 -9
- data/lib/steam/packets/s2a_rules_packet.rb +8 -12
- data/lib/steam/packets/s2c_challenge_packet.rb +7 -6
- data/lib/steam/packets/steam_packet.rb +7 -6
- data/lib/steam/packets/steam_packet_factory.rb +37 -38
- data/lib/steam/servers/game_server.rb +76 -86
- data/lib/steam/servers/goldsrc_server.rb +5 -5
- data/lib/steam/servers/master_server.rb +24 -24
- data/lib/steam/servers/source_server.rb +17 -19
- data/lib/steam/sockets/goldsrc_socket.rb +43 -46
- data/lib/steam/sockets/master_server_socket.rb +11 -13
- data/lib/steam/sockets/rcon_socket.rb +15 -17
- data/lib/steam/sockets/source_socket.rb +21 -21
- data/lib/steam/sockets/steam_socket.rb +25 -28
- data/lib/steam/steam_player.rb +3 -1
- data/lib/stringio_additions.rb +48 -0
- data/test/datagram_channel_tests.rb +8 -8
- data/test/query_tests.rb +15 -14
- data/test/rcon_tests.rb +32 -31
- data/test/socket_channel_tests.rb +7 -7
- data/test/steam/communtiy/steam_community_test_suite.rb +5 -2
- data/test/steam_community_tests.rb +4 -3
- data/test/stringio_additions_tests.rb +77 -0
- metadata +15 -8
- data/lib/byte_buffer.rb +0 -126
- data/lib/exceptions/buffer_underflow_exception.rb +0 -8
- data/test/byte_buffer_tests.rb +0 -76
@@ -1,92 +1,81 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under the
|
2
2
|
# terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
5
|
-
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
4
|
+
# Copyright (c) 2008-2010, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'abstract_class'
|
7
|
+
require 'exceptions/steam_condenser_exception'
|
8
|
+
require 'steam/steam_player'
|
9
|
+
require 'steam/packets/a2s_info_packet'
|
10
|
+
require 'steam/packets/a2a_ping_packet'
|
11
|
+
require 'steam/packets/a2s_player_packet'
|
12
|
+
require 'steam/packets/a2s_rules_packet'
|
13
|
+
require 'steam/packets/a2s_serverquery_getchallenge_packet'
|
14
|
+
require 'steam/packets/s2a_info_base_packet'
|
15
|
+
require 'steam/packets/s2a_player_packet'
|
16
|
+
require 'steam/packets/s2a_rules_packet'
|
17
|
+
require 'steam/packets/s2c_challenge_packet'
|
18
18
|
|
19
19
|
class GameServer
|
20
|
-
|
20
|
+
|
21
21
|
include AbstractClass
|
22
|
-
|
22
|
+
|
23
23
|
REQUEST_CHALLENGE = 0
|
24
24
|
REQUEST_INFO = 1
|
25
25
|
REQUEST_PLAYER = 2
|
26
26
|
REQUEST_RULES = 3
|
27
|
-
|
27
|
+
|
28
28
|
# Returns the last measured response time of this server
|
29
29
|
#
|
30
30
|
# If there's no data, update_ping is called to measure the current response
|
31
31
|
# time of the server.
|
32
32
|
#
|
33
33
|
# Whenever you want to get a new value for the ping time call update_ping.
|
34
|
-
def
|
35
|
-
if @ping
|
36
|
-
|
37
|
-
end
|
38
|
-
return @ping
|
34
|
+
def ping
|
35
|
+
update_ping if @ping.nil?
|
36
|
+
@ping
|
39
37
|
end
|
40
|
-
|
38
|
+
|
41
39
|
# Returns an array of the player's currently playing on this server.
|
42
40
|
#
|
43
41
|
# If there's no data, update_player_info is called to get the current list of
|
44
42
|
# players.
|
45
|
-
#
|
43
|
+
#
|
46
44
|
# As the players and their scores change quite often be sure to update this
|
47
45
|
# list regularly by calling update_player_info.
|
48
|
-
def
|
49
|
-
if @player_hash
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
return @player_hash
|
46
|
+
def players(rcon_password = nil)
|
47
|
+
update_player_info(rcon_password) if @player_hash.nil?
|
48
|
+
@player_hash
|
54
49
|
end
|
55
|
-
|
50
|
+
|
56
51
|
# Returns a hash of the settings applied on the server. These settings are
|
57
52
|
# also called rules.
|
58
53
|
# The hash has the format of +rule_name+ => +rule_value+
|
59
|
-
#
|
54
|
+
#
|
60
55
|
# If there's no data, update_rules_info is called to get the current list of
|
61
56
|
# rules.
|
62
57
|
#
|
63
58
|
# As the rules usually don't change often, there's almost no need to update
|
64
59
|
# this hash. But if you need to, you can achieve this by calling
|
65
60
|
# update_rules_info.
|
66
|
-
def
|
67
|
-
if @rules_hash
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
return @rules_hash
|
61
|
+
def rules
|
62
|
+
update_rules_info if @rules_hash.nil?
|
63
|
+
@rules_hash
|
72
64
|
end
|
73
|
-
|
65
|
+
|
74
66
|
# Returns a hash with basic information on the server.
|
75
|
-
#
|
67
|
+
#
|
76
68
|
# If there's no data, update_server_info is called to get up-to-date
|
77
69
|
# information.
|
78
70
|
#
|
79
71
|
# The server information usually only changes on map change and when players
|
80
72
|
# join or leave. As the latter changes can be monitored by calling
|
81
73
|
# update_player_info, there's no need to call update_server_info very often.
|
82
|
-
def
|
83
|
-
if @info_hash
|
84
|
-
|
85
|
-
end
|
86
|
-
|
87
|
-
return @info_hash
|
74
|
+
def server_info
|
75
|
+
update_server_info if @info_hash.nil?
|
76
|
+
@info_hash
|
88
77
|
end
|
89
|
-
|
78
|
+
|
90
79
|
def handle_response_for_request(request_type, repeat_on_failure = true)
|
91
80
|
begin
|
92
81
|
case request_type
|
@@ -105,39 +94,39 @@ class GameServer
|
|
105
94
|
else
|
106
95
|
raise SteamCondenserException.new("Called with wrong request type.")
|
107
96
|
end
|
108
|
-
|
109
|
-
|
110
|
-
response_packet =
|
97
|
+
|
98
|
+
send_request request_packet
|
99
|
+
response_packet = reply
|
111
100
|
|
112
101
|
if response_packet.kind_of? S2A_INFO_BasePacket
|
113
|
-
@info_hash = response_packet.
|
102
|
+
@info_hash = response_packet.info_hash
|
114
103
|
elsif response_packet.kind_of? S2A_PLAYER_Packet
|
115
104
|
@player_hash = response_packet.player_hash
|
116
105
|
elsif response_packet.kind_of? S2A_RULES_Packet
|
117
|
-
@rules_hash = response_packet.
|
106
|
+
@rules_hash = response_packet.rules_hash
|
118
107
|
elsif response_packet.kind_of? S2C_CHALLENGE_Packet
|
119
|
-
@challenge_number = response_packet.
|
108
|
+
@challenge_number = response_packet.challenge_number
|
120
109
|
else
|
121
110
|
raise SteamCondenserException.new("Response of type #{response_packet.class} cannot be handled by this method.")
|
122
111
|
end
|
123
|
-
|
112
|
+
|
124
113
|
unless response_packet.kind_of? expected_response
|
125
114
|
puts "Expected #{expected_response}, got #{response_packet.class}." if $DEBUG
|
126
|
-
|
115
|
+
handle_response_for_request(request_type, false) if repeat_on_failure
|
127
116
|
end
|
128
117
|
rescue TimeoutException
|
129
118
|
puts "Expected #{expected_response}, but timed out." if $DEBUG
|
130
119
|
end
|
131
120
|
end
|
132
|
-
|
121
|
+
|
133
122
|
def init
|
134
|
-
|
135
|
-
|
136
|
-
|
123
|
+
update_ping
|
124
|
+
update_server_info
|
125
|
+
update_challenge_number
|
137
126
|
end
|
138
127
|
|
139
128
|
def update_player_info(rcon_password = nil)
|
140
|
-
|
129
|
+
handle_response_for_request GameServer::REQUEST_PLAYER
|
141
130
|
|
142
131
|
unless rcon_password.nil? or @player_hash.empty?
|
143
132
|
rcon_auth(rcon_password)
|
@@ -152,69 +141,70 @@ class GameServer
|
|
152
141
|
end
|
153
142
|
|
154
143
|
def update_rules_info
|
155
|
-
|
144
|
+
handle_response_for_request GameServer::REQUEST_RULES
|
156
145
|
end
|
157
|
-
|
146
|
+
|
158
147
|
def update_server_info
|
159
|
-
|
148
|
+
handle_response_for_request GameServer::REQUEST_INFO
|
160
149
|
end
|
161
|
-
|
150
|
+
|
162
151
|
def update_challenge_number
|
163
|
-
|
152
|
+
handle_response_for_request GameServer::REQUEST_CHALLENGE
|
164
153
|
end
|
165
|
-
|
154
|
+
|
166
155
|
def update_ping
|
167
|
-
|
156
|
+
send_request A2A_PING_Packet.new
|
168
157
|
start_time = Time.now
|
169
|
-
|
158
|
+
reply
|
170
159
|
end_time = Time.now
|
171
|
-
|
160
|
+
@ping = (end_time - start_time) * 1000
|
172
161
|
end
|
173
|
-
|
174
|
-
# Checks whether the listening port number of the server is in a valid range
|
162
|
+
|
163
|
+
# Checks whether the listening port number of the server is in a valid range
|
175
164
|
def initialize(port_number = 27015)
|
176
165
|
unless port_number.to_i > 0 and port_number.to_i < 65536
|
177
|
-
raise ArgumentError.new(
|
166
|
+
raise ArgumentError.new('The listening port of the server has to be a number greater than 0 and less than 65536.')
|
178
167
|
end
|
179
168
|
end
|
180
|
-
|
169
|
+
|
181
170
|
def to_s
|
182
|
-
return_string =
|
183
|
-
|
171
|
+
return_string = ''
|
172
|
+
|
184
173
|
return_string << "Ping: #{@ping}\n"
|
185
174
|
return_string << "Challenge number: #{@challenge_number}\n"
|
186
|
-
|
187
|
-
|
175
|
+
|
176
|
+
unless @info_hash.nil?
|
188
177
|
return_string << "Info:\n"
|
189
178
|
@info_hash.each do |key, value|
|
190
179
|
return_string << " #{key}: #{value.inspect}\n"
|
191
180
|
end
|
192
181
|
end
|
193
|
-
|
194
|
-
|
182
|
+
|
183
|
+
unless @player_hash.nil?
|
195
184
|
return_string << "Players:\n"
|
196
185
|
@player_hash.each_value do |player|
|
197
186
|
return_string << " #{player}\n"
|
198
187
|
end
|
199
188
|
end
|
200
|
-
|
201
|
-
|
189
|
+
|
190
|
+
unless @rules_hash.nil?
|
202
191
|
return_string << "Rules:\n"
|
203
192
|
@rules_hash.each do |key, value|
|
204
193
|
return_string << " #{key}: #{value}\n"
|
205
194
|
end
|
206
195
|
end
|
207
|
-
|
208
|
-
|
196
|
+
|
197
|
+
return_string
|
209
198
|
end
|
210
199
|
|
211
200
|
protected
|
212
201
|
|
213
|
-
def
|
214
|
-
@socket.
|
202
|
+
def reply
|
203
|
+
@socket.reply
|
215
204
|
end
|
216
|
-
|
205
|
+
|
217
206
|
def send_request packet
|
218
207
|
@socket.send packet
|
219
208
|
end
|
209
|
+
|
220
210
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under the
|
2
2
|
# terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2010, Sebastian Staudt
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require 'steam/servers/game_server'
|
7
|
+
require 'steam/sockets/goldsrc_socket'
|
8
8
|
|
9
9
|
class GoldSrcServer < GameServer
|
10
10
|
|
@@ -26,11 +26,11 @@ class GoldSrcServer < GameServer
|
|
26
26
|
|
27
27
|
def rcon_auth(password)
|
28
28
|
@rcon_password = password
|
29
|
-
|
29
|
+
true
|
30
30
|
end
|
31
31
|
|
32
32
|
def rcon_exec(command)
|
33
|
-
|
33
|
+
@socket.rcon_exec(@rcon_password, command).strip
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under the
|
2
2
|
# terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2010, Sebastian Staudt
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require 'steam/packets/a2m_get_servers_batch2_packet'
|
7
|
+
require 'steam/sockets/master_server_socket'
|
8
8
|
|
9
9
|
class MasterServer
|
10
|
-
|
11
|
-
GOLDSRC_MASTER_SERVER =
|
12
|
-
SOURCE_MASTER_SERVER =
|
10
|
+
|
11
|
+
GOLDSRC_MASTER_SERVER = 'hl1master.steampowered.com', 27013
|
12
|
+
SOURCE_MASTER_SERVER = 'hl2master.steampowered.com', 27011
|
13
13
|
|
14
14
|
REGION_US_EAST_COAST = 0x00
|
15
15
|
REGION_US_WEST_COAST = 0x01
|
@@ -20,39 +20,39 @@ class MasterServer
|
|
20
20
|
REGION_MIDDLE_EAST = 0x06
|
21
21
|
REGION_AFRICA = 0x07
|
22
22
|
REGION_ALL = 0xFF
|
23
|
-
|
23
|
+
|
24
24
|
def initialize(master_server_address, master_server_port)
|
25
25
|
@socket = MasterServerSocket.new master_server_address, master_server_port
|
26
|
-
@server_array =
|
26
|
+
@server_array = []
|
27
27
|
end
|
28
|
-
|
29
|
-
def
|
30
|
-
if @server_array.empty?
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
return @server_array
|
28
|
+
|
29
|
+
def servers(region_code = MasterServer::REGION_ALL, filters = '')
|
30
|
+
update_servers region_code, filters if @server_array.empty?
|
31
|
+
@server_array
|
35
32
|
end
|
36
|
-
|
33
|
+
|
37
34
|
def update_servers(region_code, filters)
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
fail_count = 0
|
36
|
+
finished = false
|
37
|
+
current_server = '0.0.0.0:0'
|
38
|
+
|
41
39
|
begin
|
42
40
|
@socket.send A2M_GET_SERVERS_BATCH2_Packet.new(region_code, current_server, filters)
|
43
41
|
begin
|
44
|
-
servers = @socket.
|
42
|
+
servers = @socket.reply.servers
|
45
43
|
servers.each do |server|
|
46
|
-
if server ==
|
44
|
+
if server == '0.0.0.0:0'
|
47
45
|
finished = true
|
48
46
|
else
|
49
47
|
current_server = server
|
50
|
-
@server_array << server.split(
|
48
|
+
@server_array << server.split(':')
|
51
49
|
end
|
52
50
|
end
|
51
|
+
fail_count = 0
|
53
52
|
rescue TimeoutException
|
53
|
+
raise $! if (fail_count += 1) == 3
|
54
54
|
end
|
55
55
|
end while !finished
|
56
56
|
end
|
57
|
-
|
58
|
-
end
|
57
|
+
|
58
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under the
|
2
2
|
# terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2008-
|
4
|
+
# Copyright (c) 2008-2010, Sebastian Staudt
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
6
|
+
require 'exceptions/rcon_no_auth_exception'
|
7
|
+
require 'steam/packets/rcon/rcon_auth_request'
|
8
|
+
require 'steam/packets/rcon/rcon_auth_response'
|
9
|
+
require 'steam/packets/rcon/rcon_exec_request'
|
10
|
+
require 'steam/servers/game_server'
|
11
|
+
require 'steam/sockets/rcon_socket'
|
12
|
+
require 'steam/sockets/source_socket'
|
13
13
|
|
14
14
|
class SourceServer < GameServer
|
15
15
|
|
@@ -32,23 +32,21 @@ class SourceServer < GameServer
|
|
32
32
|
@rcon_request_id = rand 2**16
|
33
33
|
|
34
34
|
@rcon_socket.send RCONAuthRequest.new(@rcon_request_id, password)
|
35
|
-
@rcon_socket.
|
36
|
-
reply = @rcon_socket.
|
35
|
+
@rcon_socket.reply
|
36
|
+
reply = @rcon_socket.reply
|
37
37
|
|
38
|
-
if reply.request_id == -1
|
39
|
-
raise RCONNoAuthException.new
|
40
|
-
end
|
38
|
+
raise RCONNoAuthException.new if reply.request_id == -1
|
41
39
|
|
42
|
-
|
40
|
+
reply.request_id == @rcon_request_id
|
43
41
|
end
|
44
42
|
|
45
43
|
def rcon_exec(command)
|
46
44
|
@rcon_socket.send RCONExecRequest.new(@rcon_request_id, command)
|
47
|
-
response_packets =
|
45
|
+
response_packets = []
|
48
46
|
|
49
47
|
begin
|
50
48
|
begin
|
51
|
-
response_packet = @rcon_socket.
|
49
|
+
response_packet = @rcon_socket.reply
|
52
50
|
raise RCONNoAuthException.new if response_packet.is_a? RCONAuthResponse
|
53
51
|
response_packets << response_packet
|
54
52
|
end while true
|
@@ -56,12 +54,12 @@ class SourceServer < GameServer
|
|
56
54
|
raise $! if response_packets.empty?
|
57
55
|
end
|
58
56
|
|
59
|
-
response =
|
57
|
+
response = ''
|
60
58
|
response_packets.each do |packet|
|
61
|
-
response << packet.
|
59
|
+
response << packet.response
|
62
60
|
end
|
63
61
|
|
64
|
-
|
62
|
+
response.strip
|
65
63
|
end
|
66
64
|
|
67
65
|
end
|