steam-condenser 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Gemfile.lock +30 -0
  2. data/LICENSE +1 -1
  3. data/README.md +4 -3
  4. data/lib/steam-condenser/version.rb +2 -2
  5. data/lib/steam/community/alien_swarm/alien_swarm_mission.rb +24 -22
  6. data/lib/steam/community/alien_swarm/alien_swarm_stats.rb +66 -65
  7. data/lib/steam/community/alien_swarm/alien_swarm_weapon.rb +6 -6
  8. data/lib/steam/community/app_news.rb +2 -2
  9. data/lib/steam/community/css/css_map.rb +4 -4
  10. data/lib/steam/community/css/css_stats.rb +43 -43
  11. data/lib/steam/community/css/css_weapon.rb +5 -5
  12. data/lib/steam/community/defense_grid/defense_grid_stats.rb +36 -35
  13. data/lib/steam/community/dods/dods_class.rb +14 -14
  14. data/lib/steam/community/dods/dods_stats.rb +5 -4
  15. data/lib/steam/community/dods/dods_weapon.rb +6 -6
  16. data/lib/steam/community/game_achievement.rb +38 -31
  17. data/lib/steam/community/game_inventory.rb +6 -6
  18. data/lib/steam/community/game_leaderboard.rb +34 -32
  19. data/lib/steam/community/game_leaderboard_entry.rb +6 -6
  20. data/lib/steam/community/game_stats.rb +39 -65
  21. data/lib/steam/community/game_weapon.rb +2 -2
  22. data/lib/steam/community/l4d/abstract_l4d_stats.rb +54 -49
  23. data/lib/steam/community/l4d/abstract_l4d_weapon.rb +7 -6
  24. data/lib/steam/community/l4d/l4d2_map.rb +10 -10
  25. data/lib/steam/community/l4d/l4d2_stats.rb +33 -33
  26. data/lib/steam/community/l4d/l4d2_weapon.rb +8 -7
  27. data/lib/steam/community/l4d/l4d_explosive.rb +5 -4
  28. data/lib/steam/community/l4d/l4d_map.rb +8 -7
  29. data/lib/steam/community/l4d/l4d_stats.rb +7 -7
  30. data/lib/steam/community/l4d/l4d_weapon.rb +5 -4
  31. data/lib/steam/community/portal2/portal2_stats.rb +3 -3
  32. data/lib/steam/community/steam_game.rb +106 -16
  33. data/lib/steam/community/steam_group.rb +51 -40
  34. data/lib/steam/community/steam_id.rb +119 -87
  35. data/lib/steam/community/tf2/tf2_class.rb +14 -14
  36. data/lib/steam/community/tf2/tf2_class_factory.rb +2 -2
  37. data/lib/steam/community/tf2/tf2_engineer.rb +5 -7
  38. data/lib/steam/community/tf2/tf2_golden_wrench.rb +1 -1
  39. data/lib/steam/community/tf2/tf2_medic.rb +4 -6
  40. data/lib/steam/community/tf2/tf2_sniper.rb +3 -5
  41. data/lib/steam/community/tf2/tf2_spy.rb +10 -6
  42. data/lib/steam/community/tf2/tf2_stats.rb +15 -7
  43. data/lib/steam/community/web_api.rb +15 -1
  44. data/lib/steam/community/xml_data.rb +17 -0
  45. data/lib/steam/servers/game_server.rb +4 -4
  46. data/lib/steam/servers/master_server.rb +2 -2
  47. data/lib/steam/servers/source_server.rb +0 -2
  48. data/lib/steam/sockets/goldsrc_socket.rb +2 -2
  49. data/lib/steam/steam_player.rb +2 -2
  50. data/steam-condenser.gemspec +3 -2
  51. data/test/helper.rb +10 -2
  52. data/test/steam/communtiy/test_steam_group.rb +4 -4
  53. data/test/steam/communtiy/test_steam_id.rb +28 -2
  54. data/test/steam/communtiy/test_web_api.rb +2 -2
  55. data/test/steam/packets/test_steam_packet.rb +37 -0
  56. data/test/steam/servers/test_game_server.rb +296 -308
  57. data/test/steam/servers/test_goldsrc_server.rb +59 -59
  58. data/test/steam/servers/test_master_server.rb +131 -131
  59. data/test/steam/servers/test_server.rb +72 -72
  60. data/test/steam/servers/test_source_server.rb +126 -140
  61. data/test/steam/sockets/test_master_server_socket.rb +1 -0
  62. metadata +39 -19
@@ -8,9 +8,7 @@ require 'steam/community/tf2/tf2_class'
8
8
  # Represents the stats for the Team Fortress 2 Sniper class for a specific user
9
9
  #
10
10
  # @author Sebastian Staudt
11
- class TF2Sniper
12
-
13
- include TF2Class
11
+ class TF2Sniper < TF2Class
14
12
 
15
13
  # Returns the maximum number enemies killed with a headshot by the player in
16
14
  # a single life as a Sniper
@@ -20,11 +18,11 @@ class TF2Sniper
20
18
 
21
19
  # Creates a new instance of the Sniper class based on the given XML data
22
20
  #
23
- # @param [REXML::Element] class_data The XML data for this Sniper
21
+ # @param [Hash<String, Object>] class_data The XML data for this Sniper
24
22
  def initialize(class_data)
25
23
  super class_data
26
24
 
27
- @max_headshots = class_data.elements['iheadshots'].text.to_i
25
+ @max_headshots = class_data['iheadshots'].to_i
28
26
  end
29
27
 
30
28
  end
@@ -8,9 +8,7 @@ require 'steam/community/tf2/tf2_class'
8
8
  # Represents the stats for the Team Fortress 2 Spy class for a specific user
9
9
  #
10
10
  # @author Sebastian Staudt
11
- class TF2Spy
12
-
13
- include TF2Class
11
+ class TF2Spy < TF2Class
14
12
 
15
13
  # Returns the maximum number enemies killed with a backstab by the player in
16
14
  # a single life as a Spy
@@ -18,6 +16,11 @@ class TF2Spy
18
16
  # @return [Fixnum] Maximum number of buildings built
19
17
  attr_reader :max_backstabs
20
18
 
19
+ # Returns the head shots by the player in a single life as a Spy
20
+ #
21
+ # @return [Fixnum] Maximum number of head shots
22
+ attr_reader :max_head_shots
23
+
21
24
  # Returns the maximum health leeched from enemies by the player in a single
22
25
  # life as a Spy
23
26
  #
@@ -26,12 +29,13 @@ class TF2Spy
26
29
 
27
30
  # Creates a new instance of the Spy class based on the given XML data
28
31
  #
29
- # @param [REXML::Element] class_data The XML data for this Spy
32
+ # @param [Hash<String, Object>] class_data The XML data for this Spy
30
33
  def initialize(class_data)
31
34
  super class_data
32
35
 
33
- @max_backstabs = class_data.elements['ibackstabs'].text.to_i
34
- @max_health_leeched = class_data.elements['ihealthpointsleached'].text.to_i
36
+ @max_backstabs = class_data['ibackstabs'].to_i
37
+ @max_head_shots = class_data['iheadshots'].to_i
38
+ @max_health_leeched = class_data['ihealthpointsleached'].to_i
35
39
  end
36
40
 
37
41
  end
@@ -1,7 +1,7 @@
1
1
  # This code is free software; you can redistribute it and/or modify it under
2
2
  # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2008-2011, Sebastian Staudt
4
+ # Copyright (c) 2008-2012, Sebastian Staudt
5
5
 
6
6
  require 'steam/community/game_stats'
7
7
  require 'steam/community/tf2/tf2_beta_inventory'
@@ -19,6 +19,11 @@ class TF2Stats < GameStats
19
19
  # @return [Fixnum] This player's accumulated points
20
20
  attr_reader :accumulated_points
21
21
 
22
+ # Returns the accumulated number of seconds this player has spent playing as a TF2 class
23
+ #
24
+ # @return [Fixnum] total seconds played as a TF2 class
25
+ attr_reader :total_playtime
26
+
22
27
  # Creates a `TF2Stats` instance by calling the super constructor with the
23
28
  # game name `'tf2'`
24
29
  #
@@ -28,8 +33,11 @@ class TF2Stats < GameStats
28
33
  def initialize(steam_id, beta = false)
29
34
  super steam_id, (beta ? '520' : 'tf2')
30
35
 
31
- if public? && !@xml_data.elements['stats/accumulatedPoints'].nil?
32
- @accumulated_points = @xml_data.elements['stats/accumulatedPoints'].text.to_i
36
+ if public? && !@xml_data['stats']['accumulatedPoints'].nil?
37
+ @accumulated_points = @xml_data['stats']['accumulatedPoints'].to_i
38
+ end
39
+ if public? && !@xml_data['stats']['secondsPlayedAllClassesLifetime'].nil?
40
+ @total_playtime = @xml_data['stats']['secondsPlayedAllClassesLifetime']
33
41
  end
34
42
  end
35
43
 
@@ -44,8 +52,8 @@ class TF2Stats < GameStats
44
52
 
45
53
  if @class_stats.nil?
46
54
  @class_stats = Hash.new
47
- @xml_data.elements.each('stats/classData') do |class_data|
48
- @class_stats[class_data.elements['className'].text] = TF2ClassFactory.tf2_class(class_data)
55
+ @xml_data['stats']['classData'].each do |class_data|
56
+ @class_stats[class_data['className']] = TF2ClassFactory.tf2_class(class_data)
49
57
  end
50
58
  end
51
59
 
@@ -58,8 +66,8 @@ class TF2Stats < GameStats
58
66
  # @return [TF2Inventory] This player's TF2 backpack
59
67
  def inventory
60
68
  if @inventory.nil?
61
- inventory_class = (game_friendly_name == 'tf2') ? TF2Inventory : TF2BetaInventory
62
- @inventory = inventory_class.new(steam_id64) if @inventory.nil?
69
+ inventory_class = (game.short_name == 'tf2') ? TF2Inventory : TF2BetaInventory
70
+ @inventory = inventory_class.new(user.steam_id64) if @inventory.nil?
63
71
  end
64
72
 
65
73
  @inventory
@@ -16,6 +16,8 @@ require 'errors/web_api_error'
16
16
  # @author Sebastian
17
17
  module WebApi
18
18
 
19
+ @@api_key = nil
20
+
19
21
  # Returns the Steam Web API key currently used by Steam Condenser
20
22
  #
21
23
  # @return [String] The currently active Steam Web API key
@@ -37,6 +39,18 @@ module WebApi
37
39
  @@api_key = api_key
38
40
  end
39
41
 
42
+ # Returns a raw list of interfaces and their methods that are available in
43
+ # Steam's Web API
44
+ #
45
+ # This can be used for reference when accessing interfaces and methods that
46
+ # have not yet been implemented by Steam Condenser.
47
+ #
48
+ # @return [Array<Hash>] The list of interfaces and methods
49
+ def self.interfaces
50
+ data = json 'ISteamWebAPIUtil', 'GetSupportedAPIList'
51
+ MultiJson.load(data, { :symbolize_keys => true })[:apilist][:interfaces]
52
+ end
53
+
40
54
  # Fetches JSON data from Steam Web API using the specified interface, method
41
55
  # and version. Additional parameters are supplied via HTTP GET.
42
56
  # Data is returned as a JSON-encoded string.
@@ -67,7 +81,7 @@ module WebApi
67
81
  # @return [Hash<Symbol, Object>] The JSON data replied to the request
68
82
  def self.json!(interface, method, version = 1, params = nil)
69
83
  data = json(interface, method, version, params)
70
- result = MultiJson.decode(data, { :symbolize_keys => true })[:result]
84
+ result = MultiJson.load(data, { :symbolize_keys => true })[:result]
71
85
 
72
86
  status = result[:status]
73
87
  if status != 1
@@ -0,0 +1,17 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'open-uri'
7
+
8
+ require 'multi_xml'
9
+
10
+ module XMLData
11
+
12
+ def parse(url)
13
+ data = open(url, { :proxy => true })
14
+ @xml_data = MultiXml.parse(data).values.first
15
+ end
16
+
17
+ end
@@ -1,7 +1,7 @@
1
1
  # This code is free software; you can redistribute it and/or modify it under
2
2
  # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2008-2011, Sebastian Staudt
4
+ # Copyright (c) 2008-2012, Sebastian Staudt
5
5
 
6
6
  require 'errors/steam_condenser_error'
7
7
  require 'errors/timeout_error'
@@ -203,7 +203,7 @@ module GameServer
203
203
  def handle_response_for_request(request_type, repeat_on_failure = true)
204
204
  case request_type
205
205
  when :challenge then
206
- request_packet = A2S_SERVERQUERY_GETCHALLENGE_Packet.new
206
+ request_packet = A2S_PLAYER_Packet.new
207
207
  expected_response = S2C_CHALLENGE_Packet
208
208
  when :info then
209
209
  request_packet = A2S_INFO_Packet.new
@@ -346,8 +346,8 @@ module GameServer
346
346
  def to_s
347
347
  return_string = ''
348
348
 
349
- return_string << "Ping: #{@ping}\n"
350
- return_string << "Challenge number: #{@challenge_number}\n"
349
+ return_string << "Ping: #@ping\n"
350
+ return_string << "Challenge number: #@challenge_number\n"
351
351
 
352
352
  unless @info_hash.nil?
353
353
  return_string << "Info:\n"
@@ -28,7 +28,7 @@ class MasterServer
28
28
  # The master server address to query for GoldSrc game servers
29
29
  GOLDSRC_MASTER_SERVER = 'hl1master.steampowered.com', 27010
30
30
 
31
- # The master server address to query for GoldSrc game servers
31
+ # The master server address to query for Source game servers
32
32
  SOURCE_MASTER_SERVER = 'hl2master.steampowered.com', 27011
33
33
 
34
34
  # The region code for the US east coast
@@ -103,7 +103,7 @@ class MasterServer
103
103
  # * `\map\[mapname]`: Request only servers running a specific map
104
104
  # * `\linux\1`: Request only linux servers
105
105
  # * `\emtpy\1`: Request only **non**-empty servers
106
- # * `\full\`: Request only servers **not** full
106
+ # * `\full\1`: Request only servers **not** full
107
107
  # * `\proxy\1`: Request only spectator proxy servers
108
108
  #
109
109
  # @note Receiving all servers from the master server is taking quite some
@@ -68,8 +68,6 @@ class SourceServer
68
68
  @rcon_socket.reply
69
69
  reply = @rcon_socket.reply
70
70
 
71
- raise RCONNoAuthError if reply.request_id == -1
72
-
73
71
  @rcon_authenticated = reply.request_id == @rcon_request_id
74
72
  end
75
73
 
@@ -90,8 +90,8 @@ class GoldSrcSocket
90
90
  def rcon_exec(password, command)
91
91
  rcon_challenge if @rcon_challenge.nil? || @is_hltv
92
92
 
93
- rcon_send "rcon #{@rcon_challenge} #{password} #{command}"
94
- rcon_send "rcon #{@rcon_challenge} #{password}"
93
+ rcon_send "rcon #@rcon_challenge #{password} #{command}"
94
+ rcon_send "rcon #@rcon_challenge #{password}"
95
95
  if @is_hltv
96
96
  begin
97
97
  response = reply.response
@@ -134,9 +134,9 @@ class SteamPlayer
134
134
  # @return [String] A string representing this player
135
135
  def to_s
136
136
  if @extended
137
- "\##{@real_id} \"#{@name}\", SteamID: #{@steam_id}, Score: #{@score}, Time: #{@connect_time}"
137
+ "\##@real_id \"#@name\", SteamID: #@steam_id, Score: #@score, Time: #@connect_time"
138
138
  else
139
- "\##{@id} \"#{@name}\", Score: #{@score}, Time: #{@connect_time}"
139
+ "\##@id \"#@name\", Score: #@score, Time: #@connect_time"
140
140
  end
141
141
  end
142
142
 
@@ -11,9 +11,10 @@ Gem::Specification.new do |s|
11
11
  s.description = 'A multi-language library for querying the Steam Community, Source, GoldSrc servers and Steam master servers'
12
12
 
13
13
  s.add_dependency 'bzip2-ruby', '~> 0.2.7'
14
- s.add_dependency 'multi_json', '~> 1.0.3'
14
+ s.add_dependency 'multi_json', '~> 1.3.1'
15
+ s.add_dependency 'multi_xml', '~> 0.4.1'
15
16
 
16
- s.add_development_dependency 'mocha', '~> 0.10.0'
17
+ s.add_development_dependency 'mocha', '~> 0.11.1'
17
18
  s.add_development_dependency 'rake', '~> 0.9.2'
18
19
  s.add_development_dependency 'shoulda-context', '~> 1.0.0'
19
20
  s.add_development_dependency 'yard', '~> 0.7.2'
data/test/helper.rb CHANGED
@@ -26,12 +26,20 @@ class Test::Unit::TestCase
26
26
  assert !boolean, message
27
27
  end
28
28
 
29
- # Reads a file with fixtures from `./test/`
29
+ # Reads the contents of a fixture file from `./test/`
30
30
  #
31
31
  # @param [String] name The name of the fixtures file
32
32
  # @return [String] The contents of the file
33
33
  def fixture(name)
34
- File.read File.join(File.dirname(__FILE__), 'fixtures', name)
34
+ fixture_io(name).read
35
+ end
36
+
37
+ # Opens a file with fixtures from `./test/`
38
+ #
39
+ # @param [String] name The name of the fixtures file
40
+ # @return [File] The file handle
41
+ def fixture_io(name)
42
+ File.open File.join(File.dirname(__FILE__), 'fixtures', name)
35
43
  end
36
44
 
37
45
  end
@@ -29,7 +29,7 @@ class TestSteamGroup < Test::Unit::TestCase
29
29
  end
30
30
 
31
31
  should 'be able to fetch its members' do
32
- url = mock :read => fixture('valve-members.xml')
32
+ url = fixture_io 'valve-members.xml'
33
33
  SteamGroup.any_instance.expects(:open).with('http://steamcommunity.com/groups/valve/memberslistxml?p=1', { :proxy => true }).returns url
34
34
 
35
35
  group = SteamGroup.new 'valve'
@@ -58,7 +58,7 @@ class TestSteamGroup < Test::Unit::TestCase
58
58
 
59
59
  should 'raise an exception when parsing invalid XML' do
60
60
  error = assert_raises SteamCondenserError do
61
- url = mock :read => fixture('invalid.xml')
61
+ url = fixture_io 'invalid.xml'
62
62
  SteamGroup.any_instance.expects(:open).with('http://steamcommunity.com/groups/valve/memberslistxml?p=1', { :proxy => true }).returns url
63
63
 
64
64
  SteamGroup.new 'valve'
@@ -67,8 +67,8 @@ class TestSteamGroup < Test::Unit::TestCase
67
67
  end
68
68
 
69
69
  should 'be able to parse just the member count' do
70
- url = mock :read => fixture('valve-members.xml')
71
- SteamGroup.any_instance.expects(:open).with('http://steamcommunity.com/groups/valve/memberslistxml', { :proxy => true }).returns url
70
+ url = fixture_io 'valve-members.xml'
71
+ SteamGroup.any_instance.expects(:open).with('http://steamcommunity.com/groups/valve/memberslistxml?p=1', { :proxy => true }).returns url
72
72
 
73
73
  group = SteamGroup.new 'valve', false
74
74
  assert_equal 221, group.member_count
@@ -42,14 +42,40 @@ class TestSteamId < Test::Unit::TestCase
42
42
  assert SteamId.cached? 'son_of_Thor'
43
43
  end
44
44
 
45
+ should 'have an ID' do
46
+ steam_id1 = SteamId.new 76561197983311154, false
47
+ steam_id2 = SteamId.new 'Son_of_Thor', false
48
+
49
+ assert_equal 76561197983311154, steam_id1.id
50
+ assert_equal 'son_of_thor', steam_id2.id
51
+ end
52
+
45
53
  should 'be able to fetch its data' do
46
- url = mock :read => fixture('sonofthor.xml')
54
+ url = fixture_io 'sonofthor.xml'
47
55
  SteamId.any_instance.expects(:open).with('http://steamcommunity.com/id/son_of_thor?xml=1', { :proxy => true }).returns url
48
56
 
49
57
  steam_id = SteamId.new 'Son_of_Thor'
50
58
 
51
59
  assert_equal 76561197983311154, steam_id.steam_id64
60
+ assert_equal 'son_of_thor', steam_id.custom_url
61
+ assert_equal 'Bellevue, Washington, United States', steam_id.location
62
+ assert_equal 'Dad serious.', steam_id.head_line
63
+ assert_equal 'Son of Thor', steam_id.nickname
64
+ assert_equal 'Torsten Zabka', steam_id.real_name
65
+ assert_equal 'Last Online: 3 days ago', steam_id.state_message
66
+ assert_equal 'We jump that fence when we get to it.', steam_id.summary
67
+ assert_equal 'None', steam_id.trade_ban_state
68
+
69
+ assert_equal 'http://media.steampowered.com/steamcommunity/public/images/avatars/b8/b8438d91481295b7cc8da9578004cd63a2c3b2e4_full.jpg', steam_id.full_avatar_url
70
+ assert_equal 'http://media.steampowered.com/steamcommunity/public/images/avatars/b8/b8438d91481295b7cc8da9578004cd63a2c3b2e4.jpg', steam_id.icon_url
71
+ assert_equal 'http://media.steampowered.com/steamcommunity/public/images/avatars/b8/b8438d91481295b7cc8da9578004cd63a2c3b2e4_medium.jpg', steam_id.medium_avatar_url
72
+
73
+ assert_not steam_id.banned?
74
+ assert_not steam_id.limited?
75
+ assert_not steam_id.online?
52
76
  assert steam_id.fetched?
77
+
78
+ assert steam_id.public?
53
79
  end
54
80
 
55
81
  should 'be found by the 64bit SteamID' do
@@ -68,7 +94,7 @@ class TestSteamId < Test::Unit::TestCase
68
94
 
69
95
  should 'raise an exception when parsing invalid XML' do
70
96
  error = assert_raises SteamCondenserError do
71
- url = mock :read => fixture('invalid.xml')
97
+ url = fixture_io 'invalid.xml'
72
98
  SteamId.any_instance.expects(:open).with('http://steamcommunity.com/id/son_of_thor?xml=1', { :proxy => true }).returns url
73
99
 
74
100
  SteamId.new 'Son_of_Thor'
@@ -41,7 +41,7 @@ class TestWebApi < Test::Unit::TestCase
41
41
  data = mock
42
42
  WebApi.expects(:json).with('interface', 'method', 2, { :test => 'param' }).
43
43
  returns data
44
- MultiJson.expects(:decode).with(data, { :symbolize_keys => true }).
44
+ MultiJson.expects(:load).with(data, { :symbolize_keys => true }).
45
45
  returns({ :result => { :status => 1 }})
46
46
 
47
47
  assert_equal({ :status => 1 }, WebApi.json!('interface', 'method', 2, { :test => 'param' }))
@@ -51,7 +51,7 @@ class TestWebApi < Test::Unit::TestCase
51
51
  data = mock
52
52
  WebApi.expects(:json).with('interface', 'method', 2, { :test => 'param' }).
53
53
  returns data
54
- MultiJson.expects(:decode).with(data, { :symbolize_keys => true }).
54
+ MultiJson.expects(:load).with(data, { :symbolize_keys => true }).
55
55
  returns({ :result => { :status => 2, :statusDetail => 'error' } })
56
56
 
57
57
  error = assert_raises WebApiError do
@@ -0,0 +1,37 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2012, Sebastian Staudt
5
+
6
+ require 'helper'
7
+ require 'steam/packets/steam_packet'
8
+
9
+ class TestSteamPacket < Test::Unit::TestCase
10
+
11
+ class GenericSteamPacket
12
+ include SteamPacket
13
+ end
14
+
15
+ context 'A packet' do
16
+
17
+ setup do
18
+ @packet = GenericSteamPacket.new 0x61, 'test'
19
+ end
20
+
21
+ should 'have a data buffer' do
22
+ data = @packet.instance_variable_get(:@content_data)
23
+ assert_instance_of StringIO, data
24
+ assert_equal 'test', data.string
25
+ end
26
+
27
+ should 'know its header' do
28
+ assert_equal 0x61, @packet.instance_variable_get(:@header_data)
29
+ end
30
+
31
+ should 'have a valid byte representation' do
32
+ assert_equal "\xFF\xFF\xFF\xFFatest", @packet.to_s
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -1,308 +1,296 @@
1
- # This code is free software; you can redistribute it and/or modify it under
2
- # the terms of the new BSD License.
3
- #
4
- # Copyright (c) 2011, Sebastian Staudt
5
-
6
- require 'helper'
7
- require 'steam/servers/game_server'
8
-
9
- class TestGameServer < Test::Unit::TestCase
10
-
11
- context 'A generic game server' do
12
-
13
- class GenericGameServer
14
- include GameServer
15
- end
16
-
17
- setup do
18
- Socket.stubs(:getaddrinfo).
19
- with('game', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
20
- returns [[nil, nil, 'game', '127.0.0.1']]
21
- @socket = mock
22
-
23
- @server = GenericGameServer.new 'game', 27015
24
- @server.instance_variable_set :@socket, @socket
25
- end
26
-
27
- should 'send packets using its client socket' do
28
- packet = mock
29
- @socket.expects(:send).with packet
30
-
31
- @server.send :send_request, packet
32
- end
33
-
34
- should 'get replies using its client socket' do
35
- packet = mock
36
- @socket.expects(:reply).returns packet
37
-
38
- assert_equal packet, @server.send(:reply)
39
- end
40
-
41
- should 'be able to calculate the latency of the server' do
42
- @server.expects(:send_request).with do |packet|
43
- packet.is_a? A2S_INFO_Packet
44
- end
45
- @server.expects(:reply).with { || sleep 0.05 }
46
-
47
- @server.update_ping
48
- assert_operator @server.instance_variable_get(:@ping), :>=, 50
49
- end
50
-
51
- should 'be able to get a challenge from the server' do
52
- @server.expects(:handle_response_for_request).with :challenge
53
-
54
- @server.update_challenge_number
55
- end
56
-
57
- should 'be able to get information about server' do
58
- @server.expects(:handle_response_for_request).with :info
59
-
60
- @server.update_server_info
61
- end
62
-
63
- should 'be able to get the server rules' do
64
- @server.expects(:handle_response_for_request).with :rules
65
-
66
- @server.update_rules
67
- end
68
-
69
- should 'be able to get the players on the server' do
70
- @server.expects(:handle_response_for_request).with :players
71
-
72
- @server.update_players
73
- end
74
-
75
- should 'provide a convenience wrapper for basic server methods' do
76
- @server.expects :update_ping
77
- @server.expects :update_server_info
78
- @server.expects :update_challenge_number
79
-
80
- @server.init
81
- end
82
-
83
- should 'know if its RCON connection is authenticated' do
84
- assert_equal @server.instance_variable_get(:@rcon_authenticated),
85
- @server.rcon_authenticated?
86
- end
87
-
88
- should 'cache the ping of the server' do
89
- @server.expects :update_ping
90
-
91
- @server.ping
92
-
93
- @server.instance_variable_set :@ping, 0
94
-
95
- @server.ping
96
- end
97
-
98
- should 'cache the players on the server' do
99
- @server.expects :update_players
100
-
101
- @server.players
102
-
103
- @server.instance_variable_set :@player_hash, 0
104
-
105
- @server.players
106
- end
107
-
108
- should 'cache the server rules' do
109
- @server.expects :update_rules
110
-
111
- @server.rules
112
-
113
- @server.instance_variable_set :@rules_hash, 0
114
-
115
- @server.rules
116
- end
117
-
118
- should 'cache the server information' do
119
- @server.expects :update_server_info
120
-
121
- @server.server_info
122
-
123
- @server.instance_variable_set :@info_hash, 0
124
-
125
- @server.server_info
126
- end
127
-
128
- context 'be able to get additional information about the players on a Source server' do
129
-
130
- setup do
131
- status = fixture 'status_source'
132
-
133
- @someone = mock
134
- @somebody = mock
135
- player_hash = { 'someone' => @someone, 'somebody' => @somebody }
136
- @server.instance_variable_set :@player_hash, player_hash
137
-
138
- @server.expects(:handle_response_for_request).with :players
139
- @server.expects(:rcon_exec).with('status').returns status
140
-
141
- someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :state => 'active' }
142
- somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :state => 'active' }
143
-
144
- attributes = mock
145
- GameServer.expects(:player_status_attributes).
146
- with('userid name uniqueid score connected ping loss state').
147
- returns attributes
148
- GameServer.expects(:split_player_status).
149
- with(attributes, '1 "someone" STEAM_0:0:123456 10 3:52 12 0 active').
150
- returns somebody_data
151
- GameServer.expects(:split_player_status).
152
- with(attributes, '2 "somebody" STEAM_0:0:123457 3 2:42 34 0 active').
153
- returns someone_data
154
-
155
- @somebody.expects(:add_info).with somebody_data
156
- @someone.expects(:add_info).with someone_data
157
- end
158
-
159
- should 'with the RCON password' do
160
- @server.expects(:rcon_auth).with 'password'
161
-
162
- @server.update_players 'password'
163
- end
164
-
165
- should 'if the RCON connection is authenticated' do
166
- @server.instance_variable_set :@rcon_authenticated, true
167
-
168
- @server.update_players
169
- end
170
-
171
- end
172
-
173
- context 'be able to get additional information about the players on a GoldSrc server' do
174
-
175
- setup do
176
- status = fixture 'status_goldsrc'
177
-
178
- @someone = mock
179
- @somebody = mock
180
- player_hash = { 'someone' => @someone, 'somebody' => @somebody }
181
- @server.instance_variable_set :@player_hash, player_hash
182
-
183
- @server.expects(:handle_response_for_request).with :players
184
- @server.expects(:rcon_exec).with('status').returns status
185
-
186
- someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :adr => '0' }
187
- somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :adr => '0' }
188
-
189
- attributes = mock
190
- GameServer.expects(:player_status_attributes).
191
- with('name userid uniqueid frag time ping loss adr').
192
- returns attributes
193
- GameServer.expects(:split_player_status).
194
- with(attributes, '1 "someone" 1 STEAM_0:0:123456 10 3:52 12 0 0').
195
- returns somebody_data
196
- GameServer.expects(:split_player_status).
197
- with(attributes, '2 "somebody" 2 STEAM_0:0:123457 3 2:42 34 0 0').
198
- returns someone_data
199
-
200
- @somebody.expects(:add_info).with somebody_data
201
- @someone.expects(:add_info).with someone_data
202
- end
203
-
204
- should 'with the RCON password' do
205
- @server.expects(:rcon_auth).with 'password'
206
-
207
- @server.update_players 'password'
208
- end
209
-
210
- should 'if the RCON connection is authenticated' do
211
- @server.instance_variable_set :@rcon_authenticated, true
212
-
213
- @server.update_players
214
- end
215
-
216
- end
217
-
218
- should 'handle challenge requests' do
219
- @server.expects(:send_request).with do |packet|
220
- packet.is_a? A2S_SERVERQUERY_GETCHALLENGE_Packet
221
- end
222
-
223
- packet = mock
224
- packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
225
- packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
226
- packet.expects(:kind_of?).with(S2A_RULES_Packet).returns false
227
- packet.expects(:kind_of?).with(S2C_CHALLENGE_Packet).twice.returns true
228
-
229
- packet.expects(:challenge_number).returns 1234
230
- @server.expects(:reply).returns packet
231
-
232
- @server.handle_response_for_request :challenge
233
-
234
- assert_equal 1234, @server.instance_variable_get(:@challenge_number)
235
- end
236
-
237
- should 'handle info requests' do
238
- @server.expects(:send_request).with do |packet|
239
- packet.is_a? A2S_INFO_Packet
240
- end
241
-
242
- packet = mock
243
- packet.expects(:kind_of?).with(S2A_INFO_BasePacket).twice.returns true
244
- packet.expects(:info_hash).returns({ :test => 'test' })
245
- @server.expects(:reply).returns packet
246
-
247
- @server.handle_response_for_request :info
248
-
249
- assert_equal({ :test => 'test' }, @server.instance_variable_get(:@info_hash))
250
- end
251
-
252
- should 'server rule requests' do
253
- @server.expects(:send_request).with do |packet|
254
- packet.is_a? A2S_RULES_Packet
255
- end
256
-
257
- packet = mock
258
- packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
259
- packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
260
- packet.expects(:kind_of?).with(S2A_RULES_Packet).twice.returns true
261
- packet.expects(:rules_hash).returns({ :test => 'test' })
262
- @server.expects(:reply).returns packet
263
-
264
- @server.handle_response_for_request :rules
265
-
266
- assert_equal({ :test => 'test' }, @server.instance_variable_get(:@rules_hash))
267
- end
268
-
269
- should 'player requests' do
270
- @server.expects(:send_request).with do |packet|
271
- packet.is_a? A2S_PLAYER_Packet
272
- end
273
-
274
- packet = mock
275
- packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
276
- packet.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
277
- packet.expects(:player_hash).returns({ :test => 'test' })
278
- @server.expects(:reply).returns packet
279
-
280
- @server.handle_response_for_request :players
281
-
282
- assert_equal({ :test => 'test' }, @server.instance_variable_get(:@player_hash))
283
- end
284
-
285
- should 'handle unexpected answers and retry' do
286
- @server.expects(:send_request).twice.with do |packet|
287
- packet.is_a? A2S_PLAYER_Packet
288
- end
289
-
290
- packet1 = mock
291
- packet1.expects(:kind_of?).with(S2A_INFO_BasePacket).returns true
292
- packet1.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
293
- packet1.expects(:info_hash).returns({ :test => 'test1' })
294
- packet2 = mock
295
- packet2.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
296
- packet2.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
297
- packet2.expects(:player_hash).returns({ :test => 'test2' })
298
- @server.expects(:reply).twice.returns(packet1).returns packet2
299
-
300
- @server.handle_response_for_request :players
301
-
302
- assert_equal({ :test => 'test1' }, @server.instance_variable_get(:@info_hash))
303
- assert_equal({ :test => 'test2' }, @server.instance_variable_get(:@player_hash))
304
- end
305
-
306
- end
307
-
308
- end
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011, Sebastian Staudt
5
+
6
+ require 'helper'
7
+ require 'steam/servers/game_server'
8
+
9
+ class TestGameServer < Test::Unit::TestCase
10
+
11
+ context 'A generic game server' do
12
+
13
+ class GenericGameServer
14
+ include GameServer
15
+ end
16
+
17
+ setup do
18
+ Socket.stubs(:getaddrinfo).
19
+ with('game', 27015, Socket::AF_INET, Socket::SOCK_DGRAM).
20
+ returns [[nil, nil, 'game', '127.0.0.1']]
21
+ @socket = mock
22
+
23
+ @server = GenericGameServer.new 'game', 27015
24
+ @server.instance_variable_set :@socket, @socket
25
+ end
26
+
27
+ should 'send packets using its client socket' do
28
+ packet = mock
29
+ @socket.expects(:send).with packet
30
+
31
+ @server.send :send_request, packet
32
+ end
33
+
34
+ should 'get replies using its client socket' do
35
+ packet = mock
36
+ @socket.expects(:reply).returns packet
37
+
38
+ assert_equal packet, @server.send(:reply)
39
+ end
40
+
41
+ should 'be able to calculate the latency of the server' do
42
+ @server.expects(:send_request).with do |packet|
43
+ packet.is_a? A2S_INFO_Packet
44
+ end
45
+ @server.expects(:reply).with { || sleep 0.05 }
46
+
47
+ @server.update_ping
48
+ assert_operator @server.instance_variable_get(:@ping), :>=, 50
49
+ end
50
+
51
+ should 'be able to get a challenge from the server' do
52
+ @server.expects(:handle_response_for_request).with :challenge
53
+
54
+ @server.update_challenge_number
55
+ end
56
+
57
+ should 'be able to get information about server' do
58
+ @server.expects(:handle_response_for_request).with :info
59
+
60
+ @server.update_server_info
61
+ end
62
+
63
+ should 'be able to get the server rules' do
64
+ @server.expects(:handle_response_for_request).with :rules
65
+
66
+ @server.update_rules
67
+ end
68
+
69
+ should 'be able to get the players on the server' do
70
+ @server.expects(:handle_response_for_request).with :players
71
+
72
+ @server.update_players
73
+ end
74
+
75
+ should 'provide a convenience wrapper for basic server methods' do
76
+ @server.expects :update_ping
77
+ @server.expects :update_server_info
78
+ @server.expects :update_challenge_number
79
+
80
+ @server.init
81
+ end
82
+
83
+ should 'know if its RCON connection is authenticated' do
84
+ assert_equal @server.instance_variable_get(:@rcon_authenticated),
85
+ @server.rcon_authenticated?
86
+ end
87
+
88
+ should 'cache the ping of the server' do
89
+ @server.expects :update_ping
90
+
91
+ @server.ping
92
+
93
+ @server.instance_variable_set :@ping, 0
94
+
95
+ @server.ping
96
+ end
97
+
98
+ should 'cache the players on the server' do
99
+ @server.expects :update_players
100
+
101
+ @server.players
102
+
103
+ @server.instance_variable_set :@player_hash, 0
104
+
105
+ @server.players
106
+ end
107
+
108
+ should 'cache the server rules' do
109
+ @server.expects :update_rules
110
+
111
+ @server.rules
112
+
113
+ @server.instance_variable_set :@rules_hash, 0
114
+
115
+ @server.rules
116
+ end
117
+
118
+ should 'cache the server information' do
119
+ @server.expects :update_server_info
120
+
121
+ @server.server_info
122
+
123
+ @server.instance_variable_set :@info_hash, 0
124
+
125
+ @server.server_info
126
+ end
127
+
128
+ context 'be able to get additional information about the players on a Source server' do
129
+
130
+ setup do
131
+ status = fixture 'status_source'
132
+
133
+ @someone = mock
134
+ @somebody = mock
135
+ player_hash = { 'someone' => @someone, 'somebody' => @somebody }
136
+ @server.instance_variable_set :@player_hash, player_hash
137
+
138
+ @server.expects(:handle_response_for_request).with :players
139
+ @server.expects(:rcon_exec).with('status').returns status
140
+
141
+ someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :state => 'active' }
142
+ somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :state => 'active' }
143
+
144
+ attributes = mock
145
+ GameServer.expects(:player_status_attributes).
146
+ with('userid name uniqueid score connected ping loss state').
147
+ returns attributes
148
+ GameServer.expects(:split_player_status).
149
+ with(attributes, '1 "someone" STEAM_0:0:123456 10 3:52 12 0 active').
150
+ returns somebody_data
151
+ GameServer.expects(:split_player_status).
152
+ with(attributes, '2 "somebody" STEAM_0:0:123457 3 2:42 34 0 active').
153
+ returns someone_data
154
+
155
+ @somebody.expects(:add_info).with somebody_data
156
+ @someone.expects(:add_info).with someone_data
157
+ end
158
+
159
+ should 'with the RCON password' do
160
+ @server.expects(:rcon_auth).with 'password'
161
+
162
+ @server.update_players 'password'
163
+ end
164
+
165
+ should 'if the RCON connection is authenticated' do
166
+ @server.instance_variable_set :@rcon_authenticated, true
167
+
168
+ @server.update_players
169
+ end
170
+
171
+ end
172
+
173
+ should 'be able to get additional information about the players on a GoldSrc server with the RCON password' do
174
+ status = fixture 'status_goldsrc'
175
+
176
+ @someone = mock
177
+ @somebody = mock
178
+ player_hash = { 'someone' => @someone, 'somebody' => @somebody }
179
+ @server.instance_variable_set :@player_hash, player_hash
180
+
181
+ @server.expects(:handle_response_for_request).with :players
182
+ @server.expects(:rcon_exec).with('status').returns status
183
+
184
+ someone_data = { :name => 'someone', :userid => '1', :uniqueid => 'STEAM_0:0:123456', :score => '10', :time => '3:52', :ping => '12', :loss => '0', :adr => '0' }
185
+ somebody_data = { :name => 'somebody', :userid => '2', :uniqueid => 'STEAM_0:0:123457', :score => '3', :time => '2:42', :ping => '34', :loss => '0', :adr => '0' }
186
+
187
+ attributes = mock
188
+ GameServer.expects(:player_status_attributes).
189
+ with('name userid uniqueid frag time ping loss adr').
190
+ returns attributes
191
+ GameServer.expects(:split_player_status).
192
+ with(attributes, '1 "someone" 1 STEAM_0:0:123456 10 3:52 12 0 0').
193
+ returns somebody_data
194
+ GameServer.expects(:split_player_status).
195
+ with(attributes, '2 "somebody" 2 STEAM_0:0:123457 3 2:42 34 0 0').
196
+ returns someone_data
197
+
198
+ @somebody.expects(:add_info).with somebody_data
199
+ @someone.expects(:add_info).with someone_data
200
+
201
+ @server.expects(:rcon_auth).with 'password'
202
+
203
+ @server.update_players 'password'
204
+ end
205
+
206
+ should 'handle challenge requests' do
207
+ @server.expects(:send_request).with do |packet|
208
+ packet.is_a? A2S_PLAYER_Packet
209
+ end
210
+
211
+ packet = mock
212
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
213
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
214
+ packet.expects(:kind_of?).with(S2A_RULES_Packet).returns false
215
+ packet.expects(:kind_of?).with(S2C_CHALLENGE_Packet).twice.returns true
216
+
217
+ packet.expects(:challenge_number).returns 1234
218
+ @server.expects(:reply).returns packet
219
+
220
+ @server.handle_response_for_request :challenge
221
+
222
+ assert_equal 1234, @server.instance_variable_get(:@challenge_number)
223
+ end
224
+
225
+ should 'handle info requests' do
226
+ @server.expects(:send_request).with do |packet|
227
+ packet.is_a? A2S_INFO_Packet
228
+ end
229
+
230
+ packet = mock
231
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).twice.returns true
232
+ packet.expects(:info_hash).returns({ :test => 'test' })
233
+ @server.expects(:reply).returns packet
234
+
235
+ @server.handle_response_for_request :info
236
+
237
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@info_hash))
238
+ end
239
+
240
+ should 'handle rule requests' do
241
+ @server.expects(:send_request).with do |packet|
242
+ packet.is_a? A2S_RULES_Packet
243
+ end
244
+
245
+ packet = mock
246
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
247
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
248
+ packet.expects(:kind_of?).with(S2A_RULES_Packet).twice.returns true
249
+ packet.expects(:rules_hash).returns({ :test => 'test' })
250
+ @server.expects(:reply).returns packet
251
+
252
+ @server.handle_response_for_request :rules
253
+
254
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@rules_hash))
255
+ end
256
+
257
+ should 'handle player requests' do
258
+ @server.expects(:send_request).with do |packet|
259
+ packet.is_a? A2S_PLAYER_Packet
260
+ end
261
+
262
+ packet = mock
263
+ packet.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
264
+ packet.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
265
+ packet.expects(:player_hash).returns({ :test => 'test' })
266
+ @server.expects(:reply).returns packet
267
+
268
+ @server.handle_response_for_request :players
269
+
270
+ assert_equal({ :test => 'test' }, @server.instance_variable_get(:@player_hash))
271
+ end
272
+
273
+ should 'handle unexpected answers and retry' do
274
+ @server.expects(:send_request).twice.with do |packet|
275
+ packet.is_a? A2S_PLAYER_Packet
276
+ end
277
+
278
+ packet1 = mock
279
+ packet1.expects(:kind_of?).with(S2A_INFO_BasePacket).returns true
280
+ packet1.expects(:kind_of?).with(S2A_PLAYER_Packet).returns false
281
+ packet1.expects(:info_hash).returns({ :test => 'test1' })
282
+ packet2 = mock
283
+ packet2.expects(:kind_of?).with(S2A_INFO_BasePacket).returns false
284
+ packet2.expects(:kind_of?).with(S2A_PLAYER_Packet).twice.returns true
285
+ packet2.expects(:player_hash).returns({ :test => 'test2' })
286
+ @server.expects(:reply).twice.returns(packet1).returns packet2
287
+
288
+ @server.handle_response_for_request :players
289
+
290
+ assert_equal({ :test => 'test1' }, @server.instance_variable_get(:@info_hash))
291
+ assert_equal({ :test => 'test2' }, @server.instance_variable_get(:@player_hash))
292
+ end
293
+
294
+ end
295
+
296
+ end