steam-condenser 0.8.0 → 0.9.0

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