Skip to content

Instantly share code, notes, and snippets.

@raws
Forked from wedtm/mcquery.rb
Created May 12, 2013 15:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raws/5563951 to your computer and use it in GitHub Desktop.
Save raws/5563951 to your computer and use it in GitHub Desktop.
require 'socket'
require 'timeout'
class MCQuery
MAGIC_PREFIX = "\xFE\xFD"
PACKET_TYPE_CHALLENGE = "\x09"
PACKET_TYPE_QUERY = "\x00"
ID = "\x00\x00\x00\x00"
DEFAULTS = {
host: "localhost",
port: 25565,
}
def initialize(opts = {})
@opts = DEFAULTS.merge! opts
end
def start
@socket = UDPSocket.new
@socket.connect(@opts[:host], @opts[:port])
@socket.send(MAGIC_PREFIX + PACKET_TYPE_CHALLENGE + ID, 0)
t = @socket.recvfrom(1460)[0]
key = t[5...-1].to_i
@key = Array(key).pack('N')
end
def quick
begin
timeout(5) do
begin
s = TCPSocket.open(@opts[:host], @opts[:port])
rescue Exception => ex
return false, ex
end
s.write("\xFE")
data = s.read()
data = data.gsub("\x00".force_encoding("ASCII-8BIT"), "").gsub("\x1A".force_encoding("ASCII-8BIT"), "").gsub("\xFF".force_encoding("ASCII-8BIT"), "")
results = data.split("\xA7".force_encoding("ASCII-8BIT"))
info = {}
info[:motd] = results[0]
info[:players] = results[1]
info[:max] = results[2]
info[:timestamp] = Time.now
return info
end
rescue Exception => ex
return false, ex
end
end
def simple
begin
timeout(5) do
start
query = @socket.send(MAGIC_PREFIX + PACKET_TYPE_QUERY + ID + @key, 0)
data = @socket.recvfrom(1460)[0]
buffer = data[5...-1]
val = {}
val[:motd], val[:gametype], val[:map], val[:numplayers], val[:maxplayers], buf = buffer.split("\x00", 6)
val[:timestamp] = Time.now
return val
end
rescue Exception => ex
return false, ex
end
end
def full
begin
timeout(10) do
start
query = @socket.send(MAGIC_PREFIX + PACKET_TYPE_QUERY + ID + @key + ID, 0)
data = @socket.recvfrom(1460)[0]
buffer = data[11...-1]
items, players = buffer.split("\x00\x00\x01player_\x00\x00")
if items[0...8] == 'hostname'
items = 'motd' + items[8...-1]
end
vals = {}
items = items.split("\x00")
items.each_with_index do |key, idx|
next unless idx % 2 == 0
vals[key] = items[idx + 1]
end
vals["motd"] = vals["hostname"]
vals.delete("hostname")
vals.delete("um") if vals["um"]
players = players[0..-2] if players
if players
vals[:players] = players.split("\x00")
end
puts vals
vals["raw_plugins"] = vals["plugins"]
parts = vals["raw_plugins"].split(":")
server = parts[0].strip() if parts[0]
plugins = []
if parts.size == 2
plugins = parts[1].split(";").map {|value| value.strip() }
end
vals["plugins"] = plugins
vals["server"] = server
vals["timestamp"] = Time.now
return vals.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
end
rescue StandardError => ex
return false, ex
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment