steam-condenser 0.14.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +22 -0
  3. data/LICENSE +1 -1
  4. data/README.md +14 -6
  5. data/Rakefile +35 -0
  6. data/lib/{stringio_additions.rb → core_ext/stringio.rb} +1 -1
  7. data/lib/{exceptions/packet_format_exception.rb → errors/packet_format_error.rb} +5 -5
  8. data/lib/{exceptions/rcon_ban_exception.rb → errors/rcon_ban_error.rb} +5 -5
  9. data/lib/{exceptions/rcon_no_auth_exception.rb → errors/rcon_no_auth_error.rb} +5 -5
  10. data/lib/{exceptions/steam_condenser_exception.rb → errors/steam_condenser_error.rb} +3 -3
  11. data/lib/errors/timeout_error.rb +28 -0
  12. data/lib/{exceptions/web_api_exception.rb → errors/web_api_error.rb} +8 -8
  13. data/lib/steam/community/alien_swarm/alien_swarm_mission.rb +86 -11
  14. data/lib/steam/community/alien_swarm/alien_swarm_stats.rb +38 -15
  15. data/lib/steam/community/alien_swarm/alien_swarm_weapon.rb +29 -8
  16. data/lib/steam/community/app_news.rb +91 -27
  17. data/lib/steam/community/cacheable.rb +65 -21
  18. data/lib/steam/community/css/css_map.rb +39 -9
  19. data/lib/steam/community/css/css_stats.rb +32 -12
  20. data/lib/steam/community/css/css_weapon.rb +46 -10
  21. data/lib/steam/community/defense_grid/defense_grid_stats.rb +129 -17
  22. data/lib/steam/community/dods/dods_class.rb +66 -10
  23. data/lib/steam/community/dods/dods_stats.rb +20 -7
  24. data/lib/steam/community/dods/dods_weapon.rb +35 -24
  25. data/lib/steam/community/game_achievement.rb +50 -19
  26. data/lib/steam/community/game_class.rb +16 -5
  27. data/lib/steam/community/game_inventory.rb +37 -4
  28. data/lib/steam/community/game_item.rb +64 -3
  29. data/lib/steam/community/game_stats.rb +81 -16
  30. data/lib/steam/community/game_weapon.rb +29 -11
  31. data/lib/steam/community/l4d/abstract_l4d_stats.rb +91 -65
  32. data/lib/steam/community/l4d/abstract_l4d_weapon.rb +38 -8
  33. data/lib/steam/community/l4d/l4d2_map.rb +30 -5
  34. data/lib/steam/community/l4d/l4d2_stats.rb +83 -45
  35. data/lib/steam/community/l4d/l4d2_weapon.rb +20 -6
  36. data/lib/steam/community/l4d/l4d_explosive.rb +13 -5
  37. data/lib/steam/community/l4d/l4d_map.rb +35 -7
  38. data/lib/steam/community/l4d/l4d_stats.rb +23 -10
  39. data/lib/steam/community/l4d/l4d_weapon.rb +11 -7
  40. data/lib/steam/community/portal2/portal2_inventory.rb +4 -0
  41. data/lib/steam/community/portal2/portal2_item.rb +13 -1
  42. data/lib/steam/community/portal2/portal2_stats.rb +10 -6
  43. data/lib/steam/community/steam_game.rb +74 -0
  44. data/lib/steam/community/steam_group.rb +48 -14
  45. data/lib/steam/community/steam_id.rb +295 -64
  46. data/lib/steam/community/tf2/tf2_class.rb +66 -7
  47. data/lib/steam/community/tf2/tf2_class_factory.rb +14 -7
  48. data/lib/steam/community/tf2/tf2_engineer.rb +26 -6
  49. data/lib/steam/community/tf2/tf2_golden_wrench.rb +37 -10
  50. data/lib/steam/community/tf2/tf2_inventory.rb +4 -0
  51. data/lib/steam/community/tf2/tf2_item.rb +13 -1
  52. data/lib/steam/community/tf2/tf2_medic.rb +20 -5
  53. data/lib/steam/community/tf2/tf2_sniper.rb +15 -5
  54. data/lib/steam/community/tf2/tf2_spy.rb +20 -6
  55. data/lib/steam/community/tf2/tf2_stats.rb +20 -6
  56. data/lib/steam/community/web_api.rb +50 -32
  57. data/lib/steam/packets/c2m_checkmd5_packet.rb +1 -1
  58. data/lib/steam/packets/m2a_server_batch_packet.rb +3 -2
  59. data/lib/steam/packets/m2s_requestrestart_packet.rb +3 -3
  60. data/lib/steam/packets/rcon/rcon_packet_factory.rb +4 -4
  61. data/lib/steam/packets/request_with_challenge.rb +1 -1
  62. data/lib/steam/packets/s2a_info_base_packet.rb +1 -1
  63. data/lib/steam/packets/s2a_info_detailed_packet.rb +10 -10
  64. data/lib/steam/packets/s2a_player_packet.rb +5 -1
  65. data/lib/steam/packets/s2a_rules_packet.rb +4 -3
  66. data/lib/steam/packets/s2m_heartbeat2_packet.rb +2 -2
  67. data/lib/steam/packets/steam_packet.rb +1 -1
  68. data/lib/steam/packets/steam_packet_factory.rb +12 -16
  69. data/lib/steam/servers/game_server.rb +38 -32
  70. data/lib/steam/servers/goldsrc_server.rb +10 -1
  71. data/lib/steam/servers/master_server.rb +42 -21
  72. data/lib/steam/servers/server.rb +4 -5
  73. data/lib/steam/servers/source_server.rb +20 -5
  74. data/lib/steam/sockets/goldsrc_socket.rb +53 -22
  75. data/lib/steam/sockets/master_server_socket.rb +14 -4
  76. data/lib/steam/sockets/rcon_socket.rb +39 -6
  77. data/lib/steam/sockets/source_socket.rb +19 -15
  78. data/lib/steam/sockets/steam_socket.rb +33 -23
  79. data/lib/steam/steam_player.rb +86 -11
  80. data/lib/steam-condenser/community.rb +24 -24
  81. data/lib/steam-condenser/servers.rb +2 -2
  82. data/lib/steam-condenser/version.rb +3 -3
  83. data/steam-condenser.gemspec +23 -0
  84. data/test/query_tests.rb +12 -12
  85. data/test/rcon_tests.rb +3 -3
  86. data/test/steam/communtiy/steam_community_test_suite.rb +12 -0
  87. data/test/steam/communtiy/steam_group_tests.rb +6 -5
  88. data/test/steam/communtiy/steam_id_tests.rb +6 -5
  89. data/test/steam_community_tests.rb +3 -3
  90. data/test/stringio_additions_tests.rb +7 -7
  91. metadata +61 -43
  92. data/lib/exceptions/timeout_exception.rb +0 -24
  93. data/test/datagram_channel_tests.rb +0 -42
  94. data/test/socket_channel_tests.rb +0 -43
@@ -4,6 +4,7 @@
4
4
  # Copyright (c) 2008-2011, Sebastian Staudt
5
5
 
6
6
  require 'steam/servers/game_server'
7
+ require 'steam/servers/master_server'
7
8
  require 'steam/sockets/goldsrc_socket'
8
9
 
9
10
  # This class represents a GoldSrc game server and can be used to query
@@ -19,13 +20,21 @@ class GoldSrcServer
19
20
 
20
21
  include GameServer
21
22
 
23
+ # Returns a master server instance for the default master server for GoldSrc
24
+ # games
25
+ #
26
+ # @return [MasterServer] The GoldSrc master server
27
+ def self.master
28
+ MasterServer.new *MasterServer::GOLDSRC_MASTER_SERVER
29
+ end
30
+
22
31
  # Creates a new instance of a GoldSrc server object
23
32
  #
24
33
  # @param [String] address Either an IP address, a DNS name or one of them
25
34
  # combined with the port number. If a port number is given, e.g.
26
35
  # 'server.example.com:27016' it will override the second argument.
27
36
  # @param [Fixnum] port The port the server is listening on
28
- # @raise [SteamCondenserException] if an host name cannot be resolved
37
+ # @raise [SteamCondenserError] if an host name cannot be resolved
29
38
  # @param [Boolean] is_hltv HLTV servers need special treatment, so this is
30
39
  # used to determine if the server is a HLTV server
31
40
  def initialize(address, port = 27015, is_hltv = false)
@@ -3,6 +3,7 @@
3
3
  #
4
4
  # Copyright (c) 2008-2011, Sebastian Staudt
5
5
 
6
+ require 'errors/timeout_error'
6
7
  require 'steam/packets/a2m_get_servers_batch2_packet'
7
8
  require 'steam/packets/c2m_checkmd5_packet'
8
9
  require 'steam/packets/s2m_heartbeat2_packet'
@@ -21,6 +22,9 @@ class MasterServer
21
22
 
22
23
  include Server
23
24
 
25
+ # The default number of allowed retries
26
+ @@retries = 3
27
+
24
28
  # The master server address to query for GoldSrc game servers
25
29
  GOLDSRC_MASTER_SERVER = 'hl1master.steampowered.com', 27010
26
30
 
@@ -54,13 +58,21 @@ class MasterServer
54
58
  # The region code for the whole world
55
59
  REGION_ALL = 0xFF
56
60
 
61
+ # Sets the number of consecutive requests that may fail, before getting
62
+ # the server list is cancelled (default: 3)
63
+ #
64
+ # @param [Fixnum] The number of allowed retries
65
+ def self.retries=(retries)
66
+ @@retries = retries
67
+ end
68
+
57
69
  # Request a challenge number from the master server.
58
70
  #
59
71
  # This is used for further communication with the master server.
60
72
  #
61
73
  # @note Please note that this is **not** needed for finding servers using
62
74
  # {#servers}.
63
- # @return The challenge number from the master server
75
+ # @return [Fixnum] The challenge number from the master server
64
76
  # @see #send_heartbeat
65
77
  def challenge
66
78
  failsafe do
@@ -80,8 +92,8 @@ class MasterServer
80
92
  #
81
93
  # Filtering:
82
94
  # Instead of filtering the results sent by the master server locally, you
83
- # should at least use the following filters to narrow down the results sent by
84
- # the master server.
95
+ # should at least use the following filters to narrow down the results sent
96
+ # by the master server.
85
97
  #
86
98
  # Available filters:
87
99
  #
@@ -99,33 +111,42 @@ class MasterServer
99
111
  # @param [Fixnum] region_code The region code to specify a location of the
100
112
  # game servers
101
113
  # @param [String] filters The filters that game servers should match
114
+ # @param [Boolean] force Return a list of servers even if an error occured
115
+ # while fetching them from the master server
116
+ # @raise [SteamCondenser::TimeoutError] if too many timeouts occur while
117
+ # querying the master server
102
118
  # @return [Array<Array<String>>] A list of game servers matching the given
103
119
  # region and filters
104
120
  # @see A2M_GET_SERVERS_BATCH2_Packet
105
- def servers(region_code = MasterServer::REGION_ALL, filters = '')
121
+ # @see MasterServer.retries=
122
+ def servers(region_code = MasterServer::REGION_ALL, filters = '', force = false)
106
123
  fail_count = 0
107
124
  finished = false
108
125
  current_server = '0.0.0.0:0'
109
126
  server_array = []
110
127
 
111
- failsafe do
112
- begin
113
- @socket.send A2M_GET_SERVERS_BATCH2_Packet.new(region_code, current_server, filters)
128
+ begin
129
+ failsafe do
114
130
  begin
115
- servers = @socket.reply.servers
116
- servers.each do |server|
117
- if server == '0.0.0.0:0'
118
- finished = true
119
- else
120
- current_server = server
121
- server_array << server.split(':')
131
+ @socket.send A2M_GET_SERVERS_BATCH2_Packet.new(region_code, current_server, filters)
132
+ begin
133
+ servers = @socket.reply.servers
134
+ servers.each do |server|
135
+ if server == '0.0.0.0:0'
136
+ finished = true
137
+ else
138
+ current_server = server
139
+ server_array << server.split(':')
140
+ end
122
141
  end
142
+ fail_count = 0
143
+ rescue SteamCondenser::TimeoutError
144
+ raise $! if (fail_count += 1) == @@retries
123
145
  end
124
- fail_count = 0
125
- rescue TimeoutException
126
- raise $! if (fail_count += 1) == 3
127
- end
128
- end while !finished
146
+ end while !finished
147
+ end
148
+ rescue SteamCondenser::TimeoutError
149
+ raise $! unless force
129
150
  end
130
151
 
131
152
  server_array
@@ -136,7 +157,7 @@ class MasterServer
136
157
  # This can be used to check server versions externally.
137
158
  #
138
159
  # @param [Hash<Symbol, Object>] The data to send with the heartbeat request
139
- # @raise [SteamCondenserException] if heartbeat data is missing the
160
+ # @raise [SteamCondenserError] if heartbeat data is missing the
140
161
  # challenge number or the reply cannot be parsed
141
162
  # @return [Array<SteamPacket>] Zero or more reply packets from the server.
142
163
  # Zero means either the heartbeat was accepted by the master or there
@@ -150,7 +171,7 @@ class MasterServer
150
171
  reply_packets = []
151
172
  begin
152
173
  loop { reply_packets << @socket.reply }
153
- rescue TimeoutException
174
+ rescue SteamCondenser::TimeoutError
154
175
  end
155
176
  end
156
177
 
@@ -30,7 +30,7 @@ module Server
30
30
  # 'server.example.com:27016' it will override the second argument.
31
31
  # @param [Fixnum] port The port the server is listening on
32
32
  # @see init_socket
33
- # @raise [SteamCondenserException] if an host name cannot be resolved
33
+ # @raise [SteamCondenserError] if an host name cannot be resolved
34
34
  def initialize(address, port = nil)
35
35
  address = address.to_s
36
36
  address, port = address.split(':', 2) if address.include? ':'
@@ -79,10 +79,9 @@ module Server
79
79
 
80
80
  # Execute an action in the context of this server's current IP address
81
81
  #
82
- # Any failure, i.e. an exception, will cause the IP to rotate to the next IP
83
- # in the server's IP list and the execution will be repeated for the next IP
84
- # address. If the IP rotation reaches the end of the list, the exception will
85
- # be reraised.
82
+ # Any failure will cause the IP to rotate to the next IP in the server's IP
83
+ # list and the execution will be repeated for the next IP address. If the IP
84
+ # rotation reaches the end of the list, the error will be reraised.
86
85
  #
87
86
  # @param [Proc] proc The action to be executed in a failsafe way
88
87
  # @see #rotate_ip
@@ -3,12 +3,13 @@
3
3
  #
4
4
  # Copyright (c) 2008-2011, Sebastian Staudt
5
5
 
6
- require 'exceptions/rcon_no_auth_exception'
6
+ require 'errors/rcon_no_auth_error'
7
7
  require 'steam/packets/rcon/rcon_auth_request'
8
8
  require 'steam/packets/rcon/rcon_auth_response'
9
9
  require 'steam/packets/rcon/rcon_exec_request'
10
10
  require 'steam/packets/rcon/rcon_terminator'
11
11
  require 'steam/servers/game_server'
12
+ require 'steam/servers/master_server'
12
13
  require 'steam/sockets/rcon_socket'
13
14
  require 'steam/sockets/source_socket'
14
15
 
@@ -25,6 +26,14 @@ class SourceServer
25
26
 
26
27
  include GameServer
27
28
 
29
+ # Returns a master server instance for the default master server for Source
30
+ # games
31
+ #
32
+ # @return [MasterServer] The Source master server
33
+ def self.master
34
+ MasterServer.new *MasterServer::GOLDSRC_MASTER_SERVER
35
+ end
36
+
28
37
  # Creates a new instance of a server object representing a Source server,
29
38
  # i.e. SrcDS instance
30
39
  #
@@ -32,7 +41,7 @@ class SourceServer
32
41
  # combined with the port number. If a port number is given, e.g.
33
42
  # 'server.example.com:27016' it will override the second argument.
34
43
  # @param [Fixnum] port The port the server is listening on
35
- # @raise [SteamCondenserException] if an host name cannot be resolved
44
+ # @raise [SteamCondenserError] if an host name cannot be resolved
36
45
  def initialize(address, port = 27015)
37
46
  super
38
47
  end
@@ -50,6 +59,7 @@ class SourceServer
50
59
  #
51
60
  # @param [String] password The RCON password of the server
52
61
  # @return [Boolean] whether authentication was successful
62
+ # @see #rcon_authenticated?
53
63
  # @see #rcon_exec
54
64
  def rcon_auth(password)
55
65
  @rcon_request_id = rand 2**16
@@ -58,9 +68,9 @@ class SourceServer
58
68
  @rcon_socket.reply
59
69
  reply = @rcon_socket.reply
60
70
 
61
- raise RCONNoAuthException.new if reply.request_id == -1
71
+ raise RCONNoAuthError if reply.request_id == -1
62
72
 
63
- reply.request_id == @rcon_request_id
73
+ @rcon_authenticated = reply.request_id == @rcon_request_id
64
74
  end
65
75
 
66
76
  # Remotely executes a command on the server via RCON
@@ -69,13 +79,18 @@ class SourceServer
69
79
  # @return [String] The output of the executed command
70
80
  # @see #rcon_auth
71
81
  def rcon_exec(command)
82
+ raise RCONNoAuthError unless @rcon_authenticated
83
+
72
84
  @rcon_socket.send RCONExecRequest.new(@rcon_request_id, command)
73
85
  @rcon_socket.send RCONTerminator.new(@rcon_request_id)
74
86
 
75
87
  response = ''
76
88
  begin
77
89
  response_packet = @rcon_socket.reply
78
- raise RCONNoAuthException.new if response_packet.is_a? RCONAuthResponse
90
+ if response_packet.is_a? RCONAuthResponse
91
+ @rcon_authenticated = false
92
+ raise RCONNoAuthError
93
+ end
79
94
  response << response_packet.response
80
95
  end while response.length == 0 || response_packet.response.size > 0
81
96
 
@@ -1,52 +1,65 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
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
- require 'stringio_additions'
7
- require 'exceptions/rcon_ban_exception'
8
- require 'exceptions/rcon_no_auth_exception'
6
+ require 'core_ext/stringio'
7
+ require 'errors/rcon_ban_error'
8
+ require 'errors/rcon_no_auth_error'
9
+ require 'errors/timeout_error'
9
10
  require 'steam/packets/steam_packet_factory'
10
11
  require 'steam/packets/rcon/rcon_goldsrc_request'
11
12
  require 'steam/sockets/steam_socket'
12
13
 
13
- # The SourceSocket class is a sub class of SteamSocket respecting the
14
- # specifications of the Source query protocol.
14
+ # This class represents a socket used to communicate with game servers based on
15
+ # the GoldSrc engine (e.g. Half-Life, Counter-Strike)
16
+ #
17
+ # @author Sebastian Staudt
15
18
  class GoldSrcSocket
16
19
 
17
20
  include SteamSocket
18
21
 
22
+ # Creates a new socket to communicate with the server on the given IP address
23
+ # and port
24
+ #
25
+ # @param [String] ipaddress Either the IP address or the DNS name of the
26
+ # server
27
+ # @param [Fixnum] port_number The port the server is listening on
28
+ # @param [Boolean] is_hltv `true` if the target server is a HTLV instance.
29
+ # HLTV behaves slightly different for RCON commands, this flag
30
+ # increases compatibility.
19
31
  def initialize(ipaddress, port_number = 27015, is_hltv = false)
20
32
  super ipaddress, port_number
33
+
21
34
  @is_hltv = is_hltv
22
35
  end
23
36
 
24
- # Reads a packet from the channel. The Source query protocol specifies a
25
- # maximum packet size of 1400 byte. Greater packets will be split over several
26
- # UDP packets. This method reassembles split packets into single packet
27
- # objects.
37
+ # Reads a packet from the socket
38
+ #
39
+ # The Source query protocol specifies a maximum packet size of 1,400 bytes.
40
+ # Bigger packets will be split over several UDP packets. This method
41
+ # reassembles split packets into single packet objects.
42
+ #
43
+ # @return [SteamPacket] The packet replied from the server
28
44
  def reply
29
- bytes_read = receive_packet 1400
45
+ receive_packet 1400
30
46
 
31
47
  if @buffer.long == 0xFFFFFFFE
32
48
  split_packets = []
33
49
  begin
34
- # Parsing of split packet headers
35
50
  request_id = @buffer.long
36
51
  packet_number_and_count = @buffer.byte
37
52
  packet_count = packet_number_and_count & 0xF
38
53
  packet_number = (packet_number_and_count >> 4) + 1
39
54
 
40
- # Caching of split packet data
41
55
  split_packets[packet_number - 1] = @buffer.get
42
56
 
43
57
  puts "Received packet #{packet_number} of #{packet_count} for request ##{request_id}" if $DEBUG
44
58
 
45
- # Receiving the next packet
46
59
  if split_packets.size < packet_count
47
60
  begin
48
61
  bytes_read = receive_packet
49
- rescue TimeoutException
62
+ rescue SteamCondenser::TimeoutError
50
63
  bytes_read = 0
51
64
  end
52
65
  else
@@ -64,15 +77,25 @@ class GoldSrcSocket
64
77
  packet
65
78
  end
66
79
 
80
+ # Executes the given command on the server via RCON
81
+ #
82
+ # @param [String] password The password to authenticate with the server
83
+ # @param [String] command The command to execute on the server
84
+ # @raise [RCONBanError] if the IP of the local machine has been banned on the
85
+ # game server
86
+ # @raise [RCONNoAuthError] if the password is incorrect
87
+ # @return [RCONGoldSrcResponse] The response replied by the server
88
+ # @see #rcon_challenge
89
+ # @see #rcon_send
67
90
  def rcon_exec(password, command)
68
- rcon_challenge if @rcon_challenge.nil? or @is_hltv
91
+ rcon_challenge if @rcon_challenge.nil? || @is_hltv
69
92
 
70
93
  rcon_send "rcon #{@rcon_challenge} #{password} #{command}"
71
94
  rcon_send "rcon #{@rcon_challenge} #{password}"
72
95
  if @is_hltv
73
96
  begin
74
97
  response = reply.response
75
- rescue TimeoutException
98
+ rescue SteamCondenser::TimeoutError
76
99
  response = ''
77
100
  end
78
101
  else
@@ -80,9 +103,9 @@ class GoldSrcSocket
80
103
  end
81
104
 
82
105
  if response.strip == 'Bad rcon_password.'
83
- raise RCONNoAuthException
106
+ raise RCONNoAuthError
84
107
  elsif response.strip == 'You have been banned from this server.'
85
- raise RCONBanException
108
+ raise RCONBanError
86
109
  end
87
110
 
88
111
  begin
@@ -93,17 +116,25 @@ class GoldSrcSocket
93
116
  response
94
117
  end
95
118
 
119
+ # Requests a challenge number from the server to be used for further requests
120
+ #
121
+ # @raise [RCONBanError] if the IP of the local machine has been banned on the
122
+ # game server
123
+ # @see #rcon_send
96
124
  def rcon_challenge
97
125
  rcon_send 'challenge rcon'
98
126
  response = reply.response.strip
99
127
 
100
- if response == 'You have been banned from this server.'
101
- raise RCONBanException
128
+ if response.strip == 'You have been banned from this server.'
129
+ raise RCONBanError
102
130
  end
103
131
 
104
132
  @rcon_challenge = response[14..-1]
105
133
  end
106
134
 
135
+ # Wraps the given command in a RCON request packet and send it to the server
136
+ #
137
+ # @param [String] command The RCON command to send to the server
107
138
  def rcon_send(command)
108
139
  send RCONGoldSrcRequest.new(command)
109
140
  end
@@ -1,18 +1,28 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
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-2010, Sebastian Staudt
4
+ # Copyright (c) 2008-2011, Sebastian Staudt
5
5
 
6
+ require 'errors/packet_format_error'
6
7
  require 'steam/sockets/steam_socket'
7
8
 
9
+ # This class represents a socket used to communicate with master servers
10
+ #
11
+ # @author Sebastian Staudt
8
12
  class MasterServerSocket
9
13
 
10
14
  include SteamSocket
11
15
 
16
+ # Reads a single packet from the socket
17
+ #
18
+ # @raise [PacketFormatError] if the packet has the wrong format
19
+ # @return [SteamPacket] The packet replied from the server
12
20
  def reply
13
21
  receive_packet 1500
14
22
 
15
- raise Exception.new("Master query response has wrong packet header.") unless @buffer.long == 0xFFFFFFFF
23
+ unless @buffer.long == 0xFFFFFFFF
24
+ raise PacketFormatError, 'Master query response has wrong packet header.'
25
+ end
16
26
 
17
27
  SteamPacketFactory.packet_from_data(@buffer.get)
18
28
  end
@@ -1,5 +1,5 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
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
 
@@ -7,15 +7,29 @@ require 'ipaddr'
7
7
  require 'socket'
8
8
  require 'timeout'
9
9
 
10
- require 'exceptions/rcon_ban_exception'
10
+ require 'errors/rcon_ban_error'
11
+ require 'errors/timeout_error'
11
12
  require 'steam/packets/rcon/rcon_packet'
12
13
  require 'steam/packets/rcon/rcon_packet_factory'
13
14
  require 'steam/sockets/steam_socket'
14
15
 
16
+ # This class represents a socket used for RCON communication with game servers
17
+ # based on the Source engine (e.g. Team Fortress 2, Counter-Strike: Source)
18
+ #
19
+ # The Source engine uses a stateful TCP connection for RCON communication and
20
+ # uses an additional socket of this type to handle RCON requests.
21
+ #
22
+ # @author Sebastian Staudt
15
23
  class RCONSocket
16
24
 
17
25
  include SteamSocket
18
26
 
27
+ # Creates a new TCP socket to communicate with the server on the given IP
28
+ # address and port
29
+ #
30
+ # @param [String, IPAddr] ip Either the IP address or the DNS name of the
31
+ # server
32
+ # @param [Fixnum] port The port the server is listening on
19
33
  def initialize(ip, port)
20
34
  ip = IPSocket.getaddress(ip) unless ip.is_a? IPAddr
21
35
 
@@ -24,27 +38,46 @@ class RCONSocket
24
38
  @socket = nil
25
39
  end
26
40
 
27
- # Closes the underlying socket if it exists
41
+ # Closes the underlying TCP socket if it exists
42
+ #
43
+ # SteamSocket#close
28
44
  def close
29
45
  super unless @socket.nil?
30
46
  end
31
47
 
48
+ # Connects a new TCP socket to the server
49
+ #
50
+ # @raise [SteamCondenser::TimeoutError] if the connection could not be
51
+ # established
32
52
  def connect
33
53
  begin
34
54
  timeout(@@timeout / 1000.0) { @socket = TCPSocket.new @ip, @port }
35
55
  rescue Timeout::Error
36
- raise TimeoutException
56
+ raise SteamCondenser::TimeoutError
37
57
  end
38
58
  end
39
59
 
60
+ # Sends the given RCON packet to the server
61
+ #
62
+ # @param [RCONPacket] data_packet The RCON packet to send to the server
63
+ # @see #connect
40
64
  def send(data_packet)
41
65
  connect if @socket.nil? || @socket.closed?
42
66
 
43
67
  super
44
68
  end
45
69
 
70
+ # Reads a packet from the socket
71
+ #
72
+ # The Source RCON protocol allows packets of an arbitrary sice transmitted
73
+ # using multiple TCP packets. The data is received in chunks and concatenated
74
+ # into a single response packet.
75
+ #
76
+ # @raise [RCONBanError] if the IP of the local machine has been banned on the
77
+ # game server
78
+ # @return [RCONPacket] The packet replied from the server
46
79
  def reply
47
- raise RCONBanException if receive_packet(4) == 0
80
+ raise RCONBanError if receive_packet(4) == 0
48
81
 
49
82
  @buffer.rewind
50
83
  remaining_bytes = @buffer.long