Skip to content

Instantly share code, notes, and snippets.

@wedtm
Created February 25, 2012 04:35

Revisions

  1. wedtm created this gist Feb 25, 2012.
    78 changes: 78 additions & 0 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    #
    # Tested on 1.9.2-p290, Written by Miles Smith (WedTM)
    #

    require 'socket'

    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
    @socket = UDPSocket.new
    @socket.connect(@opts[:host], @opts[:port])
    end

    def start
    @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 simple
    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
    end

    def full
    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
    vals[:players] = players.split("\x00")
    end
    puts vals
    vals["raw_plugins"] = vals["plugins"]
    parts = vals["raw_plugins"].split(":")
    server = parts[0].strip()
    plugins = []
    if parts.size == 2
    plugins = parts[1].split(";").map {|value| value.strip() }
    end
    vals["plugins"] = plugins
    vals["server"] = server

    vals
    end
    end