steam-condenser 0.8.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.
Files changed (72) hide show
  1. data/LICENSE +25 -0
  2. data/README.md +30 -0
  3. data/Rakefile +42 -0
  4. data/lib/abstract_class.rb +40 -0
  5. data/lib/byte_buffer.rb +126 -0
  6. data/lib/datagram_channel.rb +70 -0
  7. data/lib/exceptions/buffer_underflow_exception.rb +10 -0
  8. data/lib/exceptions/packet_format_exception.rb +12 -0
  9. data/lib/exceptions/rcon_no_auth_exception.rb +14 -0
  10. data/lib/exceptions/steam_condenser_exception.rb +8 -0
  11. data/lib/exceptions/timeout_exception.rb +10 -0
  12. data/lib/socket_channel.rb +54 -0
  13. data/lib/steam-condenser.rb +3 -0
  14. data/lib/steam/community/dods/dods_class.rb +32 -0
  15. data/lib/steam/community/dods/dods_stats.rb +47 -0
  16. data/lib/steam/community/dods/dods_weapon.rb +54 -0
  17. data/lib/steam/community/game_achievement.rb +26 -0
  18. data/lib/steam/community/game_class.rb +15 -0
  19. data/lib/steam/community/game_stats.rb +92 -0
  20. data/lib/steam/community/game_weapon.rb +29 -0
  21. data/lib/steam/community/l4d/l4d_explosive.rb +23 -0
  22. data/lib/steam/community/l4d/l4d_map.rb +34 -0
  23. data/lib/steam/community/l4d/l4d_stats.rb +166 -0
  24. data/lib/steam/community/l4d/l4d_weapon.rb +23 -0
  25. data/lib/steam/community/steam_group.rb +16 -0
  26. data/lib/steam/community/steam_id.rb +212 -0
  27. data/lib/steam/community/tf2/tf2_class.rb +31 -0
  28. data/lib/steam/community/tf2/tf2_class_factory.rb +38 -0
  29. data/lib/steam/community/tf2/tf2_engineer.rb +25 -0
  30. data/lib/steam/community/tf2/tf2_medic.rb +22 -0
  31. data/lib/steam/community/tf2/tf2_sniper.rb +20 -0
  32. data/lib/steam/community/tf2/tf2_spy.rb +23 -0
  33. data/lib/steam/community/tf2/tf2_stats.rb +39 -0
  34. data/lib/steam/packets/a2a_ack_packet.rb +21 -0
  35. data/lib/steam/packets/a2a_ping_packet.rb +18 -0
  36. data/lib/steam/packets/a2m_get_servers_batch2_packet.rb +25 -0
  37. data/lib/steam/packets/a2s_info_packet.rb +18 -0
  38. data/lib/steam/packets/a2s_player_packet.rb +22 -0
  39. data/lib/steam/packets/a2s_rules_packet.rb +21 -0
  40. data/lib/steam/packets/a2s_serverquery_getchallenge_packet.rb +18 -0
  41. data/lib/steam/packets/m2a_server_batch_packet.rb +37 -0
  42. data/lib/steam/packets/rcon/rcon_auth_request.rb +16 -0
  43. data/lib/steam/packets/rcon/rcon_auth_response.rb +16 -0
  44. data/lib/steam/packets/rcon/rcon_exec_request.rb +16 -0
  45. data/lib/steam/packets/rcon/rcon_exec_response.rb +20 -0
  46. data/lib/steam/packets/rcon/rcon_goldsrc_request.rb +20 -0
  47. data/lib/steam/packets/rcon/rcon_goldsrc_response.rb +20 -0
  48. data/lib/steam/packets/rcon/rcon_packet.rb +36 -0
  49. data/lib/steam/packets/rcon/rcon_packet_factory.rb +37 -0
  50. data/lib/steam/packets/request_with_challenge.rb +16 -0
  51. data/lib/steam/packets/s2a_info2_packet.rb +48 -0
  52. data/lib/steam/packets/s2a_info_base_packet.rb +29 -0
  53. data/lib/steam/packets/s2a_info_detailed_packet.rb +50 -0
  54. data/lib/steam/packets/s2a_player_packet.rb +32 -0
  55. data/lib/steam/packets/s2a_rules_packet.rb +33 -0
  56. data/lib/steam/packets/s2c_challenge_packet.rb +23 -0
  57. data/lib/steam/packets/steam_packet.rb +48 -0
  58. data/lib/steam/packets/steam_packet_factory.rb +90 -0
  59. data/lib/steam/servers/game_server.rb +222 -0
  60. data/lib/steam/servers/goldsrc_server.rb +36 -0
  61. data/lib/steam/servers/master_server.rb +60 -0
  62. data/lib/steam/servers/source_server.rb +65 -0
  63. data/lib/steam/sockets/goldsrc_socket.rb +108 -0
  64. data/lib/steam/sockets/master_server_socket.rb +20 -0
  65. data/lib/steam/sockets/rcon_socket.rb +50 -0
  66. data/lib/steam/sockets/source_socket.rb +72 -0
  67. data/lib/steam/sockets/steam_socket.rb +63 -0
  68. data/lib/steam/steam_player.rb +51 -0
  69. data/test/query_tests.rb +71 -0
  70. data/test/rcon_tests.rb +79 -0
  71. data/test/steam_community_tests.rb +36 -0
  72. metadata +132 -0
@@ -0,0 +1,3 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
@@ -0,0 +1,32 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_class'
7
+
8
+ # Represents the stats for a DoD:S class for a specific user
9
+ class DoDSClass < GameClass
10
+
11
+ attr_reader :blocks, :bombs_defused, :bombs_planted, :captures, :deaths,
12
+ :dominations, :key, :kills, :name, :rounds_lost, :rounds_won,
13
+ :revenges
14
+
15
+ # Creates a new instance of DoDSClass based on the assigned XML data
16
+ def initialize(class_data)
17
+ @blocks = class_data.elements['blocks'].text.to_i
18
+ @bombs_defused = class_data.elements['bombsdefused'].text.to_i
19
+ @bombs_planted = class_data.elements['bombsplanted'].text.to_i
20
+ @captures = class_data.elements['captures'].text.to_i
21
+ @deaths = class_data.elements['deaths'].text.to_i
22
+ @dominations = class_data.elements['dominations'].text.to_i
23
+ @key = class_data.attributes['key']
24
+ @kills = class_data.elements['kills'].text.to_i
25
+ @name = class_data.elements["name"].text
26
+ @play_time = class_data.elements["playtime"].text.to_i
27
+ @rounds_lost = class_data.elements['roundslost'].text.to_i
28
+ @rounds_won = class_data.elements['roundswon'].text.to_i
29
+ @revenges = class_data.elements['revenges'].text.to_i
30
+ end
31
+
32
+ end
@@ -0,0 +1,47 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/dods/dods_weapon'
7
+ require 'steam/community/game_stats'
8
+
9
+ class DoDSStats < GameStats
10
+
11
+ # Creates a DoDSStats object by calling the super constructor with the game
12
+ # name "DoD:S"
13
+ def initialize(steam_id)
14
+ super steam_id, 'DoD:S'
15
+ end
16
+
17
+ # Returns a Hash of DoDSClass for this user containing all DoD:S classes.
18
+ # If the classes haven't been parsed already, parsing is done now.
19
+ def class_stats
20
+ return unless public?
21
+
22
+ if @class_stats.nil?
23
+ @class_stats = {}
24
+ @xml_data.elements.each('stats/classes/class') do |class_data|
25
+ @class_stats[class_data.attributes["key"]] = DoDSClass.new class_data
26
+ end
27
+ end
28
+
29
+ @class_stats
30
+ end
31
+
32
+ # Returns a Hash of DoDSWeapon for this user containing all DoD:S weapons.
33
+ # If the weapons haven't been parsed already, parsing is done now.
34
+ def weapon_stats
35
+ return unless public?
36
+
37
+ if @weapon_stats.nil?
38
+ @weapon_stats = {}
39
+ @xml_data.elements.each('stats/weapons/weapon') do |weapon_data|
40
+ @weapon_stats[weapon_data.attributes['key']] = DoDSWeapon.new(weapon_data)
41
+ end
42
+ end
43
+
44
+ @weapon_stats
45
+ end
46
+
47
+ end
@@ -0,0 +1,54 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_weapon'
7
+
8
+ # Represents the stats for a Day of Defeat: Source weapon for a specific user
9
+ class DoDSWeapon < GameWeapon
10
+
11
+ attr_reader :headshots, :name, :hits
12
+
13
+ # Creates a new instance of DoDSWeapon based on the assigned XML data
14
+ def initialize(weapon_data)
15
+ super weapon_data
16
+
17
+ @headshots = weapon_data.elements['headshots'].text.to_i
18
+ @id = weapon_data.attributes['key']
19
+ @name = weapon_data.elements['name'].text
20
+ @shots = weapon_data.elements['shotsfired'].text.to_i
21
+ @hits = weapon_data.elements['shotshit'].text.to_i
22
+ end
23
+
24
+ # Returns the average number of hits needed for a kill with this weapon.
25
+ # Calculates the value if needed.
26
+ def avg_hits_per_kill
27
+ if @avg_hits_per_kill.nil?
28
+ @avg_hits_per_kill = @hits.to_f / @kills
29
+ end
30
+
31
+ @avg_hits_per_kill
32
+ end
33
+
34
+ # Returns the percentage of headshots relative to the shots hit with this
35
+ # weapon. Calculates the value if needed.
36
+ def headshot_percentage
37
+ if @headshot_percentage.nil?
38
+ @headshot_percentage = @headshots.to_f / @hits
39
+ end
40
+
41
+ @headshot_percentage
42
+ end
43
+
44
+ # Returns the percentage of hits relative to the shots fired with this
45
+ # weapon. Calculates the value if needed.
46
+ def hit_percentage
47
+ if @hit_percentage.nil?
48
+ @hit_percentage = @hits.to_f / @shots
49
+ end
50
+
51
+ @hit_percentage
52
+ end
53
+
54
+ end
@@ -0,0 +1,26 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ # The GameAchievement class represents a specific achievement for a single game
7
+ # and for a single user
8
+ class GameAchievement
9
+
10
+ attr_reader :app_id, :name, :steam_id
11
+
12
+ # Creates the achievement with the given name for the given user and game and
13
+ # marks it as already done if set to true
14
+ def initialize(steam_id, app_id, name, done)
15
+ @app_id = app_id
16
+ @done = done
17
+ @name = name
18
+ @steam_id = steam_id
19
+ end
20
+
21
+ # Returns whether this achievement has been done by its owner
22
+ def done?
23
+ @done
24
+ end
25
+
26
+ end
@@ -0,0 +1,15 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'abstract_class'
7
+
8
+ # Super class for classes representing player classes
9
+ class GameClass
10
+
11
+ include AbstractClass
12
+
13
+ attr_reader :name, :play_time
14
+
15
+ end
@@ -0,0 +1,92 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ require "open-uri"
7
+ require "rexml/document"
8
+
9
+ require "steam/community/game_achievement"
10
+
11
+ class GameStats
12
+ end
13
+
14
+ require "steam/community/l4d/l4d_stats"
15
+ require "steam/community/dods/dods_stats"
16
+ require "steam/community/tf2/tf2_stats"
17
+
18
+ # The GameStats class represents the game statistics for a single user and a
19
+ # specific game
20
+ class GameStats
21
+
22
+ protected :initialize
23
+
24
+ attr_reader :app_id, :game_friendly_name, :game_name, :hours_played,
25
+ :privacy_state, :steam_id
26
+
27
+ # Creates a GameStats (or one of its subclasses) object for the given user
28
+ # depending on the game selected
29
+ def self.create_game_stats(steam_id, game_name)
30
+ case game_name
31
+ when "DoD:S":
32
+ DoDSStats.new(steam_id)
33
+ when "L4D":
34
+ L4DStats.new(steam_id)
35
+ when "TF2":
36
+ TF2Stats.new(steam_id)
37
+ else
38
+ new(steam_id, game_name)
39
+ end
40
+ end
41
+
42
+ # Creates a GameStats object and fetchs data from Steam Community for the
43
+ # given user and game
44
+ def initialize(steam_id, game_name)
45
+ @steam_id = steam_id
46
+
47
+ @xml_data = REXML::Document.new(open("http://www.steamcommunity.com/id/#{@steam_id}/stats/#{game_name}?xml=1", {:proxy => true}).read).elements["playerstats"]
48
+
49
+ @privacy_state = @xml_data.elements["privacyState"].text
50
+ if public?
51
+ @app_id = @xml_data.elements["game"].elements["gameLink"].text.match("http://store.steampowered.com/app/([1-9][0-9]+)")[1]
52
+ @game_friendly_name = @xml_data.elements["game"].elements["gameFriendlyName"].text
53
+ @game_name = @xml_data.elements["game"].elements["gameName"].text
54
+ @hours_played = @xml_data.elements["stats"].elements["hoursPlayed"].text
55
+ end
56
+ end
57
+
58
+ # Returns the achievements for this stats' user and game. If the achievements
59
+ # haven't been parsed already, parsing is done now.
60
+ def achievements
61
+ return unless public?
62
+
63
+ if @achievements.nil?
64
+ @achievements = Array.new
65
+ @xml_data.elements["achievements"].elements.each("achievement") do |achievement|
66
+ @achievements << GameAchievement.new(@steam_id, @app_id, achievement.elements["name"].text, (achievement.attributes["closed"].to_i == 1))
67
+ end
68
+
69
+ @achievements_done = @achievements.reject{ |a| !a.done? }.size
70
+ end
71
+
72
+ return @achievements
73
+ end
74
+
75
+ # Returns the count of achievements done by this player. If achievements
76
+ # haven't been parsed yet, parsing is done now.
77
+ def achievements_done
78
+ achievements if @achievements_done.nil?
79
+ @achievements_done
80
+ end
81
+
82
+ # Returns a float value representing the percentage of achievements done by
83
+ # this player.
84
+ def achievements_percentage
85
+ achievements_done.to_f / @achievements.size
86
+ end
87
+
88
+ def public?
89
+ @privacy_state == 'public'
90
+ end
91
+
92
+ end
@@ -0,0 +1,29 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'abstract_class'
7
+
8
+ # Super class for classes representing game weapons
9
+ class GameWeapon
10
+
11
+ include AbstractClass
12
+
13
+ attr_reader :kills, :id, :shots
14
+
15
+ def initialize(weapon_data)
16
+ @kills = weapon_data.elements['kills'].text.to_i
17
+ end
18
+
19
+ # Returns the average number of shots needed for a kill with this weapon.
20
+ # Calculates the value if needed.
21
+ def avg_shots_per_kill
22
+ if @avg_shots_per_kill.nil?
23
+ @avg_shots_per_kill = @shots.to_f / @kills
24
+ end
25
+
26
+ @avg_shots_per_kill
27
+ end
28
+
29
+ end
@@ -0,0 +1,23 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_weapon'
7
+
8
+ class L4DExplosive < GameWeapon
9
+
10
+ # Creates a new instance of L4DExplosive based on the assigned XML data
11
+ def initialize(weapon_data)
12
+ super weapon_data
13
+
14
+ @id = weapon_data.name
15
+ @shots = weapon_data.elements['thrown'].text.to_i
16
+ end
17
+
18
+ # Returns the average number of kills for one shot of this explosive.
19
+ def avg_kills_per_shot
20
+ 1 / avg_shots_per_kill
21
+ end
22
+
23
+ end
@@ -0,0 +1,34 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ class L4DMap
7
+
8
+ attr_reader :best_time, :id, :medal, :name, :times_played
9
+
10
+ GOLD = 1
11
+ SILVER = 2
12
+ BRONZE = 3
13
+ NONE = 0
14
+
15
+ # Creates a new instance of L4DMap based on the assigned XML data
16
+ def initialize(map_data)
17
+ @best_time = map_data.elements['besttimeseconds'].text.to_f
18
+ @id = map_data.name
19
+ @name = map_data.elements['name'].text
20
+ @times_played = map_data.elements['timesplayed'].text.to_i
21
+
22
+ case map_data.elements['medal'].text
23
+ when 'gold':
24
+ @medal = L4DMap::GOLD
25
+ when 'silver':
26
+ @medal = L4DMap::SILVER
27
+ when 'bronze':
28
+ @medal = L4DMap::BRONZE
29
+ else
30
+ @medal = L4DMap::NONE
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,166 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_stats'
7
+ require 'steam/community/l4d/l4d_explosive'
8
+ require 'steam/community/l4d/l4d_map'
9
+ require 'steam/community/l4d/l4d_weapon'
10
+
11
+ class L4DStats < GameStats
12
+
13
+ attr_reader :xml_data
14
+
15
+ # Creates a L4DStats object by calling the super constructor with the game
16
+ # name "l4d"
17
+ def initialize(steam_id)
18
+ super steam_id, 'l4d'
19
+
20
+ if public?
21
+ @most_recent_game = {}
22
+ @most_recent_game['difficulty'] = @xml_data.elements['stats/mostrecentgame/difficulty'].text
23
+ @most_recent_game['escaped'] = (@xml_data.elements['stats/mostrecentgame/bEscaped'].text == 1)
24
+ @most_recent_game['movie'] = @xml_data.elements['stats/mostrecentgame/movie'].text
25
+ @most_recent_game['time_played'] = @xml_data.elements['stats/mostrecentgame/time'].text
26
+ end
27
+ end
28
+
29
+ # Returns a Hash of favorites for this user like weapons and character.
30
+ # If the favorites haven't been parsed already, parsing is done now.
31
+ def favorites
32
+ return unless public?
33
+
34
+ if @favorites.nil?
35
+ @favorites = {}
36
+ @favorites['campaign'] = @xml_data.elements['stats/favorites/campaign'].text
37
+ @favorites['campaign_percentage'] = @xml_data.elements['stats/favorites/campaignpct'].text.to_i
38
+ @favorites['character'] = @xml_data.elements['stats/favorites/character'].text
39
+ @favorites['character_percentage'] = @xml_data.elements['stats/favorites/characterpct'].text.to_i
40
+ @favorites['level1_weapon'] = @xml_data.elements['stats/favorites/weapon1'].text
41
+ @favorites['level1_weapon_percentage'] = @xml_data.elements['stats/favorites/weapon1pct'].text.to_i
42
+ @favorites['level2_weapon'] = @xml_data.elements['stats/favorites/weapon2'].text
43
+ @favorites['level2_weapon_percentage'] = @xml_data.elements['stats/favorites/weapon2pct'].text.to_i
44
+ end
45
+
46
+ @favorites
47
+ end
48
+
49
+ # Returns a Hash of lifetime statistics for this user like the time played.
50
+ # If the lifetime statistics haven't been parsed already, parsing is done now.
51
+ def lifetime_stats
52
+ return unless public?
53
+
54
+ if @lifetime_stats.nil?
55
+ @lifetime_stats = {}
56
+ @lifetime_stats['finales_survived'] = @xml_data.elements['stats/lifetime/finales'].text.to_i
57
+ @lifetime_stats['games_played'] = @xml_data.elements['stats/lifetime/gamesplayed'].text.to_i
58
+ @lifetime_stats['finales_survived_percentage'] = @lifetime_stats['finales_survived'].to_f / @lifetime_stats['games_played']
59
+ @lifetime_stats['infected_killed'] = @xml_data.elements['stats/lifetime/infectedkilled'].text.to_i
60
+ @lifetime_stats['kills_per_hour'] = @xml_data.elements['stats/lifetime/killsperhour'].text.to_f
61
+ @lifetime_stats['avg_kits_shared'] = @xml_data.elements['stats/lifetime/kitsshared'].text.to_f
62
+ @lifetime_stats['avg_kits_used'] = @xml_data.elements['stats/lifetime/kitsused'].text.to_f
63
+ @lifetime_stats['avg_pills_shared'] = @xml_data.elements['stats/lifetime/pillsshared'].text.to_f
64
+ @lifetime_stats['avg_pills_used'] = @xml_data.elements['stats/lifetime/pillsused'].text.to_f
65
+ @lifetime_stats['time_played'] = @xml_data.elements['stats/lifetime/timeplayed'].text
66
+ end
67
+
68
+ @lifetime_stats
69
+ end
70
+
71
+ # Returns a Hash of Survival statistics for this user like revived teammates.
72
+ # If the Survival statistics haven't been parsed already, parsing is done now.
73
+ def survival_stats
74
+ return unless public?
75
+
76
+ if @survival_stats.nil?
77
+ @survival_stats = {}
78
+ @survival_stats['gold_medals'] = @xml_data.elements['stats/survival/goldmedals'].text.to_i
79
+ @survival_stats['silver_medals'] = @xml_data.elements['stats/survival/silvermedals'].text.to_i
80
+ @survival_stats['bronze_medals'] = @xml_data.elements['stats/survival/bronzemedals'].text.to_i
81
+ @survival_stats['rounds_played'] = @xml_data.elements['stats/survival/roundsplayed'].text.to_i
82
+ @survival_stats['best_time'] = @xml_data.elements['stats/survival/besttime'].text.to_f
83
+
84
+ @survival_stats['maps'] = {}
85
+ @xml_data.elements.each('stats/survival/maps/*') do |map_data|
86
+ @survival_stats['maps'][map_data.name] = L4DMap.new(map_data)
87
+ end
88
+ end
89
+
90
+ @survival_stats
91
+ end
92
+
93
+ # Returns a Hash of teamplay statistics for this user like revived teammates.
94
+ # If the teamplay statistics haven't been parsed already, parsing is done now.
95
+ def teamplay_stats
96
+ return unless public?
97
+
98
+ if @teamplay_stats.nil?
99
+ @teamplay_stats = {}
100
+ @teamplay_stats['revived'] = @xml_data.elements['stats/teamplay/revived'].text.to_i
101
+ @teamplay_stats['most_revived_difficulty'] = @xml_data.elements['stats/teamplay/reviveddiff'].text
102
+ @teamplay_stats['avg_revived'] = @xml_data.elements['stats/teamplay/revivedavg'].text.to_f
103
+ @teamplay_stats['avg_was_revived'] = @xml_data.elements['stats/teamplay/wasrevivedavg'].text.to_f
104
+ @teamplay_stats['protected'] = @xml_data.elements['stats/teamplay/protected'].text.to_i
105
+ @teamplay_stats['most_protected_difficulty'] = @xml_data.elements['stats/teamplay/protecteddiff'].text
106
+ @teamplay_stats['avg_protected'] = @xml_data.elements['stats/teamplay/protectedavg'].text.to_f
107
+ @teamplay_stats['avg_was_protected'] = @xml_data.elements['stats/teamplay/wasprotectedavg'].text.to_f
108
+ @teamplay_stats['friendly_fire_damage'] = @xml_data.elements['stats/teamplay/ffdamage'].text.to_i
109
+ @teamplay_stats['most_friendly_fire_difficulty'] = @xml_data.elements['stats/teamplay/ffdamagediff'].text
110
+ @teamplay_stats['avg_friendly_fire_damage'] = @xml_data.elements['stats/teamplay/ffdamageavg'].text.to_f
111
+ end
112
+
113
+ @teamplay_stats
114
+ end
115
+
116
+ # Returns a Hash of Versus statistics for this user like percentage of rounds
117
+ # won.
118
+ # If the Versus statistics haven't been parsed already, parsing is done now.
119
+ def versus_stats
120
+ return unless public?
121
+
122
+ if @versus_stats.nil?
123
+ @versus_stats = {}
124
+ @versus_stats['games_played'] = @xml_data.elements['stats/versus/gamesplayed'].text.to_i
125
+ @versus_stats['games_completed'] = @xml_data.elements['stats/versus/gamescompleted'].text.to_i
126
+ @versus_stats['finales_survived'] = @xml_data.elements['stats/versus/finales'].text.to_i
127
+ @versus_stats['finales_survived_percentage'] = @versus_stats['finales_survived'].to_f / @versus_stats['games_played']
128
+ @versus_stats['points'] = @xml_data.elements['stats/versus/points'].text.to_i
129
+ @versus_stats['most_points_infected'] = @xml_data.elements['stats/versus/pointsas'].text
130
+ @versus_stats['games_won'] = @xml_data.elements['stats/versus/gameswon'].text.to_i
131
+ @versus_stats['games_lost'] = @xml_data.elements['stats/versus/gameslost'].text.to_i
132
+ @versus_stats['highest_survivor_score'] = @xml_data.elements['stats/versus/survivorscore'].text.to_i
133
+
134
+ %w(boomer hunter smoker tank).each do |infected|
135
+ @versus_stats[infected] = {}
136
+ @versus_stats[infected]['special'] = @xml_data.elements["stats/versus/#{infected}special"].text.to_i
137
+ @versus_stats[infected]['most_damage'] = @xml_data.elements["stats/versus/#{infected}dmg"].text.to_i
138
+ @versus_stats[infected]['avg_lifespan'] = @xml_data.elements["stats/versus/#{infected}lifespan"].text.to_i
139
+ end
140
+ end
141
+
142
+ @versus_stats
143
+ end
144
+
145
+ # Returns a Hash of L4DWeapon for this user containing all Left4Dead weapons.
146
+ # If the weapons haven't been parsed already, parsing is done now.
147
+ def weapon_stats
148
+ return unless public?
149
+
150
+ if @weapon_stats.nil?
151
+ @weapon_stats = {}
152
+ @xml_data.elements.each('stats/weapons/*') do |weapon_data|
153
+ unless weapon_data.name == 'molotov' or weapon_data.name == 'pipes'
154
+ weapon = L4DWeapon.new(weapon_data)
155
+ else
156
+ weapon = L4DExplosive.new(weapon_data)
157
+ end
158
+
159
+ @weapon_stats[weapon_data.name] = weapon
160
+ end
161
+ end
162
+
163
+ @weapon_stats
164
+ end
165
+
166
+ end