steam-condenser 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -13,15 +13,18 @@ test_files = Dir.glob(File.join("test", "**", "*.rb"))
13
13
 
14
14
  # Gem specification
15
15
  Jeweler::Tasks.new do |s|
16
- s.name = "steam-condenser"
17
- s.date = Time.now
18
- s.description = s.summary = 'A multi-language library for querying Source, GoldSrc servers and Steam master servers'
19
16
  s.authors = ['Sebastian Staudt']
20
17
  s.email = 'koraktor@gmail.com'
21
- s.homepage = 'http://github.com/koraktor/steam-condenser'
22
-
23
- s.files = %w(README.md Rakefile LICENSE) + src_files + test_files
18
+ s.description = 'A multi-language library for querying the Steam Community, Source, GoldSrc servers and Steam master servers'
19
+ s.date = Time.now
20
+ s.homepage = 'http://koraktor.github.com/steam-condenser'
21
+ s.name = s.rubyforge_project = "steam-condenser"
22
+ s.summary = 'Steam Condenser - A Steam query library'
23
+
24
+ s.files = %w(README.md Rakefile LICENSE VERSION.yml) + src_files + test_files
24
25
  s.rdoc_options = ["--all", "--inline-source", "--line-numbers", "--charset=utf-8", "--webcvs=http://github.com/koraktor/steam-condenser/source/blob/master/ruby/%s"]
26
+
27
+ s.add_dependency('hpricot', '>= 0.6')
25
28
  end
26
29
 
27
30
  # Create a rake task +:rdoc+ to build the documentation
@@ -31,7 +34,7 @@ Rake::RDocTask.new do |rdoc|
31
34
  rdoc.rdoc_files.include ["lib/**/*.rb", "test/**/*.rb", "LICENSE", "README.md"]
32
35
  rdoc.main = "README.md"
33
36
  rdoc.rdoc_dir = "rdoc"
34
- rdoc.options = ["--all", "--inline-source", "--line-numbers", "--charset=utf-8", "--webcvs=http://github.com/koraktor/steam-condenser/source/blob/master/ruby/%s"]
37
+ rdoc.options = ["--all", "--inline-source", "--line-numbers", "--charset=utf-8", "--webcvs=http://github.com/koraktor/steam-condenser/blob/master/ruby/%s"]
35
38
  end
36
39
 
37
40
  # Task for cleaning documentation and package directories
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 9
4
+ :patch: 0
data/lib/byte_buffer.rb CHANGED
@@ -1,7 +1,7 @@
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, Sebastian Staudt
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
5
 
6
6
  require "exceptions/buffer_underflow_exception"
7
7
 
@@ -11,71 +11,71 @@ class ByteBuffer
11
11
  attr_accessor :limit
12
12
  attr_reader :position
13
13
  protected :initialize
14
-
14
+
15
15
  # Creates a new buffer with the given size in bytes
16
16
  def self.allocate(length)
17
17
  return ByteBuffer.new(0.chr * length)
18
18
  end
19
-
20
- # Creates a new buffer from an existing String
19
+
20
+ # Creates a new buffer from an existing String
21
21
  def self.wrap(byte_array)
22
22
  return ByteBuffer.new(byte_array.to_s)
23
23
  end
24
-
25
- #
24
+
25
+ #
26
26
  def initialize(byte_array)
27
27
  if byte_array.is_a? ByteBuffer
28
28
  raise Exception.new
29
29
  end
30
-
30
+
31
31
  @byte_array = byte_array.to_s
32
32
  @capacity = @byte_array.length
33
33
  @limit = @capacity
34
34
  @position = 0
35
35
  @mark = -1
36
36
  end
37
-
37
+
38
38
  def array
39
39
  return @byte_array
40
40
  end
41
-
41
+
42
42
  def clear
43
43
  @limit = @capacity
44
44
  @position = 0
45
45
  @mark = -1
46
-
46
+
47
47
  return self
48
48
  end
49
-
49
+
50
50
  def flip
51
51
  @limit = @position
52
52
  @position = 0
53
53
  @mark = -1
54
-
54
+
55
55
  return self
56
56
  end
57
-
57
+
58
58
  def get(length = nil)
59
59
  if length == nil
60
60
  length = @limit - @position
61
61
  elsif length > self.remaining
62
62
  BufferUnderflowException.new
63
63
  end
64
-
64
+
65
65
  data = @byte_array[@position, length]
66
66
  @position += length
67
-
67
+
68
68
  return data
69
69
  end
70
-
70
+
71
71
  def get_byte
72
72
  return self.get(1)[0]
73
73
  end
74
-
74
+
75
75
  def get_float
76
76
  return self.get(4).unpack("e")[0]
77
77
  end
78
-
78
+
79
79
  def get_long
80
80
  return self.get(4).unpack("V")[0]
81
81
  end
@@ -87,39 +87,39 @@ class ByteBuffer
87
87
  def get_signed_long
88
88
  return self.get(4).unpack('l')[0]
89
89
  end
90
-
90
+
91
91
  def get_string
92
92
  zero_byte_index = @byte_array.index("\0", @position)
93
93
  if zero_byte_index == nil
94
- return ""
94
+ nil
95
95
  else
96
96
  data_string = self.get(zero_byte_index - @position)
97
- @position += 1
98
- return data_string
97
+ @position += 1
98
+ data_string
99
99
  end
100
100
  end
101
-
101
+
102
102
  def length
103
103
  return @byte_array.length
104
104
  end
105
-
105
+
106
106
  def remaining
107
107
  return @limit - @position
108
108
  end
109
-
109
+
110
110
  def rewind
111
111
  @position = 0
112
112
  @mark = -1
113
-
113
+
114
114
  return self
115
115
  end
116
-
116
+
117
117
  def put(source_byte_array)
118
118
  new_position = [source_byte_array.length, self.remaining].min
119
119
 
120
120
  @byte_array[@position, new_position] = source_byte_array
121
121
  @position = new_position
122
-
122
+
123
123
  return self
124
124
  end
125
125
 
@@ -1,11 +1,12 @@
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, Sebastian Staudt
5
- #
6
- # $Id$
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ require 'ipaddr'
7
+ require 'socket'
7
8
 
8
- require "byte_buffer"
9
+ require 'byte_buffer'
9
10
 
10
11
  class DatagramChannel
11
12
 
@@ -0,0 +1,12 @@
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 RCONBanException < Exception
7
+
8
+ def initialize
9
+ super 'You have been banned from this server.'
10
+ end
11
+
12
+ end
@@ -3,52 +3,59 @@
3
3
  #
4
4
  # Copyright (c) 2008-2009, Sebastian Staudt
5
5
 
6
- require 'byte_buffer'
6
+ require 'ipaddr'
7
+ require 'socket'
7
8
  require 'timeout'
8
9
 
10
+ require 'byte_buffer'
11
+
9
12
  class SocketChannel
10
-
13
+
11
14
  attr_reader :socket
12
-
15
+
13
16
  def self.open
14
17
  return SocketChannel.new
15
18
  end
16
-
19
+
20
+ def close
21
+ @socket.close
22
+ end
23
+
17
24
  def connect(*args)
18
25
  timeout(1) do
19
26
  @socket = TCPSocket.new args[0][0][3], args[0][0][1]
20
27
  @connected = true
21
28
  end
22
-
29
+
23
30
  return self
24
31
  end
25
-
32
+
26
33
  def initialize
27
34
  @connected = false
28
35
  end
29
-
36
+
30
37
  def connected?
31
38
  return @connected
32
39
  end
33
-
40
+
34
41
  def read(destination_buffer)
35
42
  if !destination_buffer.is_a? ByteBuffer
36
43
  raise ArgumentError
37
44
  end
38
-
45
+
39
46
  length = destination_buffer.remaining
40
47
  data = @socket.recv length
41
48
  destination_buffer.put data
42
-
49
+
43
50
  return data.length
44
51
  end
45
-
52
+
46
53
  def write(source_buffer)
47
54
  if !source_buffer.is_a? ByteBuffer
48
55
  raise ArgumentError
49
56
  end
50
-
57
+
51
58
  return @socket.send(source_buffer.get, 0)
52
59
  end
53
-
54
- end
60
+
61
+ end
@@ -1,3 +1,14 @@
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
+
1
6
  libdir = File.dirname(__FILE__)
2
7
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
8
 
9
+ module SteamCondenser
10
+
11
+ version = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'VERSION.yml'))
12
+ VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
13
+
14
+ end
@@ -0,0 +1,95 @@
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
+ # This module implements caching functionality to be used in any object using a
7
+ # +fetch+ method to fetch data, e.g. using a HTTP download.
8
+ module Cacheable
9
+
10
+ def self.included(base) #:nodoc:
11
+
12
+ base.extend ClassMethods
13
+ base.class_eval 'class_variable_set :@@cache, {}'
14
+ base.class_eval 'class_variable_set :@@cache_ids, {}'
15
+
16
+ class << base
17
+ alias_method :create, :new
18
+ end
19
+
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ # Defines wich instance variables should be used to index the cached objects
25
+ # A call to this method is needed, if you want a class including this module
26
+ # to really use the cache.
27
+ def cacheable_with_ids(*ids)
28
+ class_variable_set(:@@cache_ids, ids)
29
+ end
30
+
31
+ # Returns whether the requested object +id+ is already cached
32
+ def cached?(id)
33
+ if id.is_a? String
34
+ id.downcase!
35
+ end
36
+ class_variable_get(:@@cache).key?(id)
37
+ end
38
+
39
+ # Clears the object cache
40
+ def clear_cache
41
+ class_variable_set :@@cache, {}
42
+ end
43
+
44
+ # This checks the cache for an existing object. If it exists it is returned.
45
+ # Otherwise a new object is created.
46
+ # Overrides the default constructor.
47
+ def new(id, fetch = true, bypass_cache = false)
48
+ if cached?(id) and !bypass_cache
49
+ object = class_variable_get(:@@cache)[id]
50
+ object.fetch_data if fetch and !object.fetched?
51
+ object
52
+ else
53
+ super(id, fetch)
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ attr_reader :fetch_time
60
+
61
+ # Creates a new object for the given +id+, either numeric or
62
+ # the custom URL specified by the user. If +fetch+ is +true+ (default),
63
+ # fetch is used to load data into the object.
64
+ # This method is overridden by Cacheable::ClassMethods#new.
65
+ def initialize(fetch = true) #:notnew:
66
+ self.fetch if fetch
67
+ cache
68
+ end
69
+
70
+ # Saves this object in the cache
71
+ def cache
72
+ cache = self.class.class_eval 'class_variable_get :@@cache'
73
+ cache_ids = self.class.class_eval 'class_variable_get :@@cache_ids'
74
+
75
+ cache_ids.each do |cache_id|
76
+ cache_id_value = instance_variable_get('@' + cache_id.to_s)
77
+ unless cache_id_value.nil? or cache.key?(cache_id_value)
78
+ cache[cache_id_value] = self
79
+ end
80
+ end
81
+
82
+ true
83
+ end
84
+
85
+ # Sets the time this object has been fetched the last time
86
+ def fetch
87
+ @fetch_time = Time.now
88
+ end
89
+
90
+ # Returns whether the data for this SteamID has already been fetched
91
+ def fetched?
92
+ !@fetch_time.nil?
93
+ end
94
+
95
+ end
@@ -0,0 +1,124 @@
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
+
8
+ # The DefenseGridStats class represents the game statistics for a single user in
9
+ # Defense Grid: The Awakening
10
+ class DefenseGridStats < GameStats
11
+
12
+ attr_reader :bronze_medals, :damage_done, :damage_campaign, :damage_challenge,
13
+ :encountered, :gold_medals, :heat_damage, :interest, :killed,
14
+ :killed_campaign, :killed_challenge, :levels_played,
15
+ :levels_played_campaign, :levels_played_challenge, :levels_won,
16
+ :levels_won_campaign, :levels_won_challenge, :orbital_laser_fired,
17
+ :orbital_laser_damage, :resources, :silver_medals, :time_played
18
+
19
+ # Creates a DefenseGridStats object by calling the super constructor with the
20
+ # game name "defensegrid:awakening"
21
+ def initialize(steam_id)
22
+ super(steam_id, 'defensegrid:awakening')
23
+
24
+ if public?
25
+ general_data = @xml_data.elements['stats/general']
26
+
27
+ @bronze_medals = general_data.elements['bronze_medals_won/value'].text.to_i
28
+ @silver_medals = general_data.elements['silver_medals_won/value'].text.to_i
29
+ @gold_medals = general_data.elements['gold_medals_won/value'].text.to_i
30
+ @levels_played = general_data.elements['levels_played_total/value'].text.to_i
31
+ @levels_played_campagin = general_data.elements['levels_played_campaign/value'].text.to_i
32
+ @levels_played_challenge = general_data.elements['levels_played_challenge/value'].text.to_i
33
+ @levels_won = general_data.elements['levels_won_total/value'].text.to_i
34
+ @levels_won_campaign = general_data.elements['levels_won_campaign/value'].text.to_i
35
+ @levels_won_challenge = general_data.elements['levels_won_challenge/value'].text.to_i
36
+ @encountered = general_data.elements['total_aliens_encountered/value'].text.to_i
37
+ @killed = general_data.elements['total_aliens_killed/value'].text.to_i
38
+ @killed_campaign = general_data.elements['total_aliens_killed_campaign/value'].text.to_i
39
+ @killed_challenge = general_data.elements['total_aliens_killed_challenge/value'].text.to_i
40
+ @resources = general_data.elements['resources_recovered/value'].text.to_i
41
+ @heat_damage = general_data.elements['heatdamage/value'].text.to_f
42
+ @time_played = general_data.elements['time_played/value'].text.to_f
43
+ @interest = general_data.elements['interest_gained/value'].text.to_f
44
+ @damage = general_data.elements['tower_damage_total/value'].text.to_f
45
+ @damage_campaign = general_data.elements['tower_damage_total_campaign/value'].text.to_f
46
+ @damage_challenge = general_data.elements['tower_damage_total_challenge/value'].text.to_f
47
+ @orbital_laser_fired = @xml_data.elements['stats/orbitallaser/fired/value'].text.to_i
48
+ @orbital_laser_damage = @xml_data.elements['stats/orbitallaser/damage/value'].text.to_f
49
+ end
50
+ end
51
+
52
+ # Returns stats about the towers built
53
+ #
54
+ # The Hash returned uses the names of the aliens as keys. Every value of the
55
+ # Hash is an Array containing the number of aliens encountered as the first
56
+ # element and the number of aliens killed as the second element.
57
+ def alien_stats
58
+ return unless public?
59
+
60
+ if @alien_stats.nil?
61
+ alien_data = @xml_data.elements['stats/aliens']
62
+ @alien_stats = {}
63
+ aliens = %w{swarmer juggernaut crasher spire grunt bulwark drone manta dart
64
+ decoy rumbler seeker turtle walker racer stealth}
65
+
66
+ aliens.each do |alien|
67
+ @alien_stats[alien] = [
68
+ alien_data.elements["#{alien}/encountered/value"].text.to_i,
69
+ alien_data.elements["#{alien}/killed/value"].text.to_i
70
+ ]
71
+ end
72
+ end
73
+
74
+ @alien_stats
75
+ end
76
+
77
+ # Returns stats about the towers built
78
+ #
79
+ # The Hash returned uses the names of the towers as keys. Every value of
80
+ # the Hash is another Hash using the keys 1 to 3 for different tower levels.
81
+ # The values of these Hash is an Array containing the number of towers built
82
+ # as the first element and the damage dealt by this specific tower type as the
83
+ # second element.
84
+ #
85
+ # The Command tower uses the resources gained as second element.
86
+ # The Temporal tower doesn't have a second element.
87
+ def tower_stats
88
+ return unless public?
89
+
90
+ if @tower_stats.nil?
91
+ tower_data = @xml_data.elements['stats/towers']
92
+ @tower_stats = {}
93
+ towers = %w{cannon flak gun inferno laser meteor missile tesla}
94
+
95
+ towers.each do |tower|
96
+ @tower_stats[tower] = {}
97
+ (1..3).each do |i|
98
+ @tower_stats[tower][i] = [
99
+ tower_data.elements["#{tower}[@level=#{i}]/built/value"].text.to_i,
100
+ tower_data.elements["#{tower}[@level=#{i}]/damage/value"].text.to_f
101
+ ]
102
+ end
103
+ end
104
+
105
+ @tower_stats['command'] = {}
106
+ (1..3).each do |i|
107
+ @tower_stats['command'][i] = [
108
+ tower_data.elements["command[@level=#{i}]/built/value"].text.to_i,
109
+ tower_data.elements["command[@level=#{i}]/resource/value"].text.to_f
110
+ ]
111
+ end
112
+
113
+ @tower_stats['temporal'] = {}
114
+ (1..3).each do |i|
115
+ @tower_stats['temporal'][i] = [
116
+ tower_data.elements["temporal[@level=#{i}]/built/value"].text.to_i,
117
+ ]
118
+ end
119
+ end
120
+
121
+ @tower_stats
122
+ end
123
+
124
+ end