# encoding: utf-8 # GameSpy query class by Sickboy (sb_at_dev-heaven.net) =begin # TODO: Use other conditions, like gamestate if possible no mission: gameState: "1", gamemode: openwaiting Assignment: gameState: "3", numplayers: "0", gamemode: openwaiting Briefing: gamestate: "6", gamemode: openplaying Playing: gameState: "7" =end require 'socket' require 'timeout' require 'yaml' module Six module Query class Gamespy < Base attr_reader :gamedata SPLIT, PACKET = "\x00", [0xFE, 0xFD, 0x00, 0x04, 0x05, 0x06, 0x07, 0xFF, 0xFF, 0xFF].pack("c*") def initialize(host, port, number = 1) @host, @port = host, port end def sync @gamedata, key = Hash.new, nil reply = self.fetch return @gamedata unless reply gamedata = reply[0] gamedata.split(SPLIT).each_with_index do |c, i| if ((i + 1) % 2) > 0 key = c else c = clean(c) c = c.to_s if key == "hostname" # FIXME: Bah! @gamedata[key] = c end end @gamedata["players"] = [] if @gamedata["numplayers"].to_i > 0 if reply.size > 1 players = reply[1] colums = "player_\x00team_\x00score_\x00deaths_\x00\x00" players.gsub!(colums, '') players.gsub!(/\A./, '') player, nplayers = nil, [] players.split(SPLIT).each_with_index do |c, i| r = ((i + 1) % 4) case r when 1 player = Hash.new nplayers << player player["name"] = clean(c).to_s when 2 player["team"] = clean(c).to_s when 3 c += player["score"] if player["score"] player["score"] = clean(c) when 0 c += player["deaths"] if player["deaths"] player["deaths"] = clean(c) end end @gamedata["players"] = nplayers @gamedata["players"].sort! {|a, b| a["name"].downcase <=> b["name"].downcase } end end @gamedata end def fetch s = UDPSocket.new s.connect(@host, @port) s.puts(PACKET) status, reply = nil, nil begin status = Timeout::timeout(4) do reply = s.recvfrom(4096)[0] end rescue Timeout::Error raise TimeoutError ensure s.close end return nil unless reply reply.gsub!("\x00\x04\x05\x06\a", '') reply.split(SPLIT * 3) end end end end if $0 == __FILE__ host = ARGV[0] port = ARGV[1] g = Six::Query::Gamespy.new(host, port) r = g.sync Process.exit unless r puts r.to_yaml end