steam-condenser 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|