gamespy_query 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,4 @@
1
1
  #require 'six/tools'
2
- require 'action_controller'
3
2
  require 'logger'
4
3
 
5
4
  module GamespyQuery
@@ -12,12 +11,29 @@ module GamespyQuery
12
11
 
13
12
  module Tools
14
13
  STR_EMPTY = ""
14
+ CHAR_N = "\n"
15
15
 
16
16
  module_function
17
17
  def logger
18
- @logger ||= ActionController::Base.logger || Logger.new("logger.log")
18
+ @logger ||= if defined?(::Tools); ::Tools.logger; else; defined?(ActionController) ? ActionController::Base.logger || Logger.new("logger.log") : Logger.new("logger.log"); end
19
19
  end
20
-
20
+
21
+ def dbg_msg(e)
22
+ "#{e.class}: #{e.message if e.respond_to?(:backtrace)}
23
+ BackTrace: #{e.backtrace.join(CHAR_N) unless !e.respond_to?(:backtrace) || e.backtrace.nil?}"
24
+ end
25
+
26
+
27
+ def log_exception(e, as_error = true, msg = "")
28
+ if defined?(::Tools)
29
+ ::Tools.log_exception(e, as_error, msg)
30
+ else
31
+ puts "Error: #{e.class} #{e.message}, #{e.backtrace.join("\n")}"
32
+ logger.error "#{"#{msg}:" unless msg.empty?}#{e.class} #{e.message}" if as_error
33
+ logger.debug dbg_msg(e)
34
+ end
35
+ end
36
+
21
37
  def debug(&block)
22
38
  return unless DEBUG
23
39
  logger.debug yield
@@ -59,7 +75,7 @@ module GamespyQuery
59
75
  end
60
76
 
61
77
  def get_string(*params)
62
- puts "Getting string #{params}"
78
+ Tools.debug {"Getting string #{params}"}
63
79
  _get_string(*params)
64
80
  end
65
81
 
@@ -67,14 +83,14 @@ module GamespyQuery
67
83
  include System::Net
68
84
  include System::Net::Sockets
69
85
 
70
- def _get_string(str)
71
- str.map {|e| e.chr}.join # begin; System::Text::Encoding.USASCII.GetString(reply[0]).to_s; rescue nil, Exception => e; Tools.log_exception(e); reply[0].map {|e| e.chr}.join; end
86
+ def get_string(str)
87
+ System::Text::Encoding.UTF8.GetString(System::Array.of(System::Byte).new(str.bytes.to_a)).to_s # # begin; System::Text::Encoding.USASCII.GetString(reply[0]).to_s; rescue nil, Exception => e; Tools.log_exception(e); reply[0].map {|e| e.chr}.join; end
72
88
  end
73
89
  else
74
90
  require 'socket'
75
91
  require 'timeout'
76
-
77
- def _get_string(str)
92
+ def get_string(str)
93
+ #(str + ' ').encode("UTF-8", :invalid => :replace, :undef => :replace)[0..-2]
78
94
  str
79
95
  end
80
96
  end
@@ -5,7 +5,7 @@ module GamespyQuery
5
5
  PARAMS = [:hostname, :gamever, :gametype, :gamemode, :numplayers, :maxplayers, :password, :equalModRequired, :mission, :mapname,
6
6
  :mod, :signatures, :verifysignatures, :gamestate, :dedicated, :platform, :sv_battleeye, :language, :difficulty]
7
7
 
8
- # TODO: Gspy v3 multipacket
8
+ RX_ADDR_LINE = /([\d\.]+)[\s\t]*(\d+)/
9
9
 
10
10
  DELIMIT = case RUBY_PLATFORM
11
11
  when /-mingw32$/, /-mswin32$/
@@ -38,7 +38,7 @@ module GamespyQuery
38
38
  addrs = []
39
39
  list = %x[gslist -n #{@game}] if list.nil?
40
40
  list.split("\n").each do |line|
41
- addrs << "#{$1}:#{$2}" if line =~ /([\d\.]+)[\s\t]*(\d+)/
41
+ addrs << "#{$1}:#{$2}" if line =~ RX_ADDR_LINE
42
42
  end
43
43
  addrs
44
44
  end
@@ -46,8 +46,7 @@ module GamespyQuery
46
46
  def read
47
47
  geo = @geo ? @geo : "-Q 11 "
48
48
  unless File.exists?(File.join(geoip_path, "GeoIP.dat"))
49
- puts
50
- puts "Warning: GeoIP.dat database missing. Can't parse countries. #{geoip_path}"
49
+ Tools.logger.warn "Warning: GeoIP.dat database missing. Can't parse countries. #{geoip_path}"
51
50
  geo = nil
52
51
  end
53
52
  reply = %x[gslist -p "#{geoip_path}" -n #{@game} #{geo}-X #{PARAMS.clone.map{|e| "#{DELIMIT}#{e}"}.join("")}]
@@ -67,7 +66,7 @@ module GamespyQuery
67
66
  i = 0
68
67
  content.map! do |e|
69
68
  i += 1
70
- i % 2 == 0 ? e : clean(e)
69
+ i % 2 == 0 ? e : clean_string(e)
71
70
  end
72
71
  addr = "#{ip}:#{port}"
73
72
  if @list.has_key?(addr)
@@ -10,7 +10,7 @@
10
10
  require_relative 'base'
11
11
 
12
12
  module GamespyQuery
13
- class Parser
13
+ class Parser < Base
14
14
  STR_SPLIT = STR_X0
15
15
  STR_ID = "\x00\x04\x05\x06\a"
16
16
 
@@ -76,17 +76,6 @@ module GamespyQuery
76
76
  data
77
77
  end
78
78
 
79
- if RUBY_PLATFORM =~ /mswin32/
80
- def get_string(str)
81
- System::Text::Encoding.UTF8.GetString(System::Array.of(System::Byte).new(str.bytes.to_a)).to_s
82
- end
83
- else
84
- def get_string(str)
85
- #(str + ' ').encode("UTF-8", :invalid => :replace, :undef => :replace)[0..-2]
86
- str
87
- end
88
- end
89
-
90
79
  def clean_packet(packet)
91
80
  packet = packet.clone
92
81
  packet.sub!(STR_ID, STR_EMPTY) # Cut off the identity
@@ -99,7 +88,7 @@ module GamespyQuery
99
88
  get_string(packet)
100
89
  end
101
90
 
102
- def clean(str)
91
+ def clean_string(str)
103
92
  str.encode("UTF-8", invalid: :replace, undef: :replace)
104
93
  end
105
94
 
@@ -111,9 +100,9 @@ module GamespyQuery
111
100
 
112
101
  packet.split(STR_SPLIT).each_with_index do |data, index|
113
102
  if (index % 2) == 0
114
- key = clean data
103
+ key = clean_string data
115
104
  else
116
- game_data[key] = data.is_a?(String) ? clean(data) : data
105
+ game_data[key] = data.is_a?(String) ? clean_string(data) : data
117
106
  end
118
107
  end
119
108
 
@@ -171,7 +160,7 @@ module GamespyQuery
171
160
  # Parse the data - \x00 is printed after a non-nil entry, otherwise \x00 means nil (e.g empty team)
172
161
  until str.empty?
173
162
  entry = str[RX_X0_SPEC]
174
- player_data[player_data.keys[i]] << clean(entry.sub(STR_X0, STR_EMPTY))
163
+ player_data[player_data.keys[i]] << clean_string(entry.sub(STR_X0, STR_EMPTY))
175
164
  str.sub!(entry, STR_EMPTY)
176
165
  end
177
166
 
@@ -1,32 +1,31 @@
1
1
  # encoding: utf-8
2
2
  # GameSpy query class by Sickboy [Patrick Roza] (sb_at_dev-heaven.net)
3
3
 
4
-
5
-
6
4
  require 'yaml'
7
5
  require_relative 'base'
8
6
  require_relative 'parser'
9
7
  require 'socket'
10
8
 
11
9
  module GamespyQuery
10
+ # TODO
12
11
  module MultiSocket
13
12
  def create_socket(*params)
14
- puts "Creating socket #{params}"
13
+ Tools.debug {"Creating socket #{params}"}
15
14
  _create_socket(*params)
16
15
  end
17
16
 
18
17
  def socket_send(*params)
19
- puts "Sending socket #{params}"
18
+ Tools.debug {"Sending socket #{params}"}
20
19
  _socket_send(*params)
21
20
  end
22
21
 
23
22
  def socket_receive(*params)
24
- puts "Receiving socket #{params}"
23
+ Tools.debug {"Receiving socket #{params}"}
25
24
  _socket_receive(*params)
26
25
  end
27
26
 
28
27
  def socket_close(*params)
29
- puts "Closing socket #{params}"
28
+ Tools.debug {"Closing socket #{params}"}
30
29
  _socket_close(*params)
31
30
  end
32
31
 
@@ -136,32 +135,32 @@ module GamespyQuery
136
135
  def valid?; @state == STATE_READY; end
137
136
 
138
137
  def handle_write
139
- #STDOUT.puts "Write: #{self.inspect}, #{self.state}"
138
+ #Tools.debug {"Write: #{self.inspect}, #{self.state}"}
140
139
 
141
140
  r = true
142
141
  begin
143
142
  case self.state
144
143
  when STATE_INIT
145
- STDOUT.puts "Write (0): #{self.inspect}"
144
+ Tools.debug {"Write (0): #{self.inspect}"}
146
145
  # Send Challenge
147
146
  self.puts @packet
148
147
  self.state = STATE_SENT_CHALLENGE
149
148
  when STATE_RECEIVED_CHALLENGE
150
- STDOUT.puts "Write (2): #{self.inspect}"
149
+ Tools.debug {"Write (2): #{self.inspect}"}
151
150
  # Send Challenge response
152
151
  self.puts self.needs_challenge ? BASE_PACKET + @id_packet + self.needs_challenge + FULL_INFO_PACKET_MP : BASE_PACKET + @id_packet + FULL_INFO_PACKET_MP
153
152
  self.state = STATE_SENT_CHALLENGE_RESPONSE
154
153
  end
155
154
  rescue => e
156
- STDOUT.puts "Error: #{e.message}, #{self.inspect}"
155
+ Tools.log_exception e
157
156
  self.failed = true
158
157
  r = false
159
- close
158
+ close unless closed?
160
159
  end
161
160
 
162
161
  =begin
163
162
  if Time.now - self.stamp > @timeout
164
- STDOUT.puts "TimedOut: #{self.inspect}"
163
+ Tools.debug {"TimedOut: #{self.inspect}"}
165
164
  self.failed = true
166
165
  r = false
167
166
  close unless closed?
@@ -171,28 +170,28 @@ module GamespyQuery
171
170
  end
172
171
 
173
172
  def handle_read
174
- #STDOUT.puts "Read: #{self.inspect}, #{self.state}"
173
+ # Tools.debug {"Read: #{self.inspect}, #{self.state}"}
175
174
 
176
175
  r = true
177
176
  case self.state
178
177
  when STATE_SENT_CHALLENGE
179
178
  begin
180
179
  data = self.recvfrom_nonblock(RECEIVE_SIZE)
181
- STDOUT.puts "Read (1): #{self.inspect}: #{data}"
180
+ Tools.debug {"Read (1): #{self.inspect}: #{data}"}
182
181
 
183
182
  handle_challenge get_string(data[0])
184
183
 
185
184
  self.state = STATE_RECEIVED_CHALLENGE
186
185
  rescue => e
187
- STDOUT.puts "Error: #{e.message}, #{self.inspect}"
186
+ Tools.log_exception e
188
187
  self.failed = true
189
188
  r = false
190
- close
189
+ close unless closed?
191
190
  end
192
191
  when STATE_SENT_CHALLENGE_RESPONSE, STATE_RECEIVE_DATA
193
192
  begin
194
193
  data = self.recvfrom_nonblock(RECEIVE_SIZE)
195
- STDOUT.puts "Read (3,4): #{self.inspect}: #{data}"
194
+ Tools.debug {"Read (3,4): #{self.inspect}: #{data}"}
196
195
  self.state = STATE_RECEIVE_DATA
197
196
 
198
197
  game_data = get_string(data[0])
@@ -203,30 +202,29 @@ module GamespyQuery
203
202
  self.data[index] = game_data
204
203
 
205
204
  if self.data.size >= self.max_packets # OR we received the end-packet and all packets required
206
- STDOUT.puts "Received packet limit: #{self.inspect}"
205
+ Tools.debug {"Received packet limit: #{self.inspect}"}
207
206
  self.state = STATE_READY
208
207
  r = false
209
208
  close unless closed?
210
209
  end
211
210
  rescue => e
212
- STDOUT.puts "Error: #{e.message}, #{self.inspect}"
211
+ Tools.log_exception(e)
213
212
  self.failed = true
214
213
  r = false
215
- close
214
+ close unless closed?
216
215
  end
217
216
  end
218
217
  r
219
218
  end
220
219
 
221
220
  def handle_exc
222
- STDOUT.puts "Exception: #{self.inspect}"
223
- close
221
+ Tools.debug {"Exception: #{self.inspect}"}
222
+ close unless closed?
224
223
  self.failed = true
225
224
 
226
225
  false
227
226
  end
228
227
 
229
-
230
228
  def handle_splitnum game_data
231
229
  index = 0
232
230
  if game_data.sub(STR_GARBAGE, STR_EMPTY)[RX_SPLITNUM]
@@ -236,7 +234,7 @@ module GamespyQuery
236
234
  last = flag & 0x80 > 0
237
235
  # Data could be received out of order, use the "index" id when "last" flag is true, to determine total packet_count
238
236
  self.max_packets = index + 1 if last # update the max
239
- STDOUT.puts "Splitnum: #{splitnum.inspect} (#{splitnum}) (#{flag}, #{index}, #{last}) Max: #{self.max_packets}"
237
+ Tools.debug {"Splitnum: #{splitnum.inspect} (#{splitnum}) (#{flag}, #{index}, #{last}) Max: #{self.max_packets}"}
240
238
  else
241
239
  self.max_packets = 1
242
240
  end
@@ -245,7 +243,7 @@ module GamespyQuery
245
243
  end
246
244
 
247
245
  def handle_challenge str
248
- #STDOUT.puts "Received challenge response (#{str.length}): #{str.inspect}"
246
+ # Tools.debug{"Received challenge response (#{str.length}): #{str.inspect}"}
249
247
  need_challenge = !(str.sub(STR_X0, STR_EMPTY) =~ RX_NO_CHALLENGE)
250
248
  if need_challenge
251
249
  str = str.sub(RX_CHALLENGE, STR_EMPTY).gsub(RX_CHALLENGE2, STR_EMPTY).to_i
@@ -257,8 +255,8 @@ module GamespyQuery
257
255
  def handle_state; [STATE_INIT, STATE_RECEIVED_CHALLENGE].include? state; end
258
256
 
259
257
  # Supports challenge/response and multi-packet
260
- def sync
261
- game_data, key, reply = {}, nil, self.fetch
258
+ def sync reply = self.fetch
259
+ game_data, key = {}, nil
262
260
  return game_data if reply.nil?
263
261
 
264
262
  parser = Parser.new(reply)
@@ -297,12 +295,13 @@ module GamespyQuery
297
295
  pings_c = 0
298
296
  pings.each { |ping| pings_c += ping }
299
297
 
300
- ping = pings.size == 0 ? nil : pings_c / pings.sizeha
298
+ ping = pings.size == 0 ? nil : pings_c / pings.size
301
299
  Tools.debug{"Gamespy pings: #{pings}, #{ping}"}
302
300
  @ping = ping
303
301
  rescue => e
304
- STDOUT.puts "Error during fetch #{self.inspect}: #{e.message}"
302
+ Tools.log_exception(e)
305
303
  r = nil
304
+ close unless closed?
306
305
  end
307
306
  r
308
307
  end
@@ -35,13 +35,13 @@ module GamespyQuery
35
35
  write_sockets, read_sockets = queue.reject {|s| s.valid? }.partition {|s| s.handle_state }
36
36
 
37
37
  unless ready = IO.select(read_sockets, write_sockets, nil, @timeout)
38
- puts "Timeout, no usable sockets in current queue, within timeout period"
38
+ Tools.logger.warn "Timeout, no usable sockets in current queue, within timeout period (#{@timeout}s)"
39
39
  queue.each{|s| s.close unless s.closed?}
40
40
  queue = []
41
41
  next
42
42
  end
43
43
 
44
- puts "Sockets: #{queue.size}, AddrsLeft: #{@addrs.size}, ReadReady: #{"#{ready[0].size} / #{read_sockets.size}, WriteReady: #{ready[1].size} / #{write_sockets.size}, ExcReady: #{ready[2].size} / #{queue.size}" unless ready.nil?}"
44
+ Tools.debug {"Sockets: #{queue.size}, AddrsLeft: #{@addrs.size}, ReadReady: #{"#{ready[0].size} / #{read_sockets.size}, WriteReady: #{ready[1].size} / #{write_sockets.size}, ExcReady: #{ready[2].size} / #{queue.size}" unless ready.nil?}"}
45
45
 
46
46
  # Read
47
47
  ready[0].each { |s| queue.delete(s) unless s.handle_read() }
@@ -1,3 +1,3 @@
1
1
  module GamespyQuery
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/gamespy_query.rb CHANGED
@@ -10,10 +10,16 @@ end
10
10
 
11
11
 
12
12
  if $0 == __FILE__
13
- host = ARGV[0]
14
- port = ARGV[1]
15
- g = GamespyQuery::Socket.new(host, port)
13
+ host, port = if ARGV.size > 1
14
+ ARGV
15
+ else
16
+ ARGV[0].split(":")
17
+ end
18
+ time_start = Time.now
19
+ g = GamespyQuery::Socket.new("#{host}:#{port}")
16
20
  r = g.sync
21
+ time_taken = Time.now - time_start
22
+ puts "Took: #{time_taken}s"
17
23
  exit unless r
18
24
  puts r.to_yaml
19
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamespy_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: