gamespy_query 0.1.0 → 0.1.1

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.
@@ -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: