minestat 2.2.4 → 3.0.0
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.
- checksums.yaml +4 -4
- data/.yardopts +8 -0
- data/ChangeLog.md +55 -0
- data/License.txt +674 -0
- data/ReadMe.md +60 -0
- data/example.rb +18 -0
- data/lib/minestat.rb +425 -171
- metadata +16 -8
    
        data/lib/minestat.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            # minestat.rb - A Minecraft server status checker
         | 
| 2 | 
            -
            # Copyright (C) 2014- | 
| 2 | 
            +
            # Copyright (C) 2014-2023 Lloyd Dilley
         | 
| 3 3 | 
             
            # http://www.dilley.me/
         | 
| 4 4 | 
             
            #
         | 
| 5 5 | 
             
            # This program is free software; you can redistribute it and/or modify
         | 
| @@ -18,78 +18,135 @@ | |
| 18 18 |  | 
| 19 19 | 
             
            require 'base64'
         | 
| 20 20 | 
             
            require 'json'
         | 
| 21 | 
            +
            require 'resolv'
         | 
| 21 22 | 
             
            require 'socket'
         | 
| 22 23 | 
             
            require 'timeout'
         | 
| 23 24 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 25 | 
            +
            # @author Lloyd Dilley
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            # Provides a Ruby interface for polling the status of Minecraft servers
         | 
| 26 28 | 
             
            class MineStat
         | 
| 27 29 | 
             
              # MineStat version
         | 
| 28 | 
            -
              VERSION = " | 
| 30 | 
            +
              VERSION = "3.0.0"
         | 
| 31 | 
            +
             | 
| 29 32 | 
             
              # Number of values expected from server
         | 
| 30 33 | 
             
              NUM_FIELDS = 6
         | 
| 31 | 
            -
               | 
| 34 | 
            +
              private_constant :NUM_FIELDS
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              # Number of values expected from a 1.8b - 1.3 server
         | 
| 32 37 | 
             
              NUM_FIELDS_BETA = 3
         | 
| 38 | 
            +
              private_constant :NUM_FIELDS_BETA
         | 
| 39 | 
            +
             | 
| 33 40 | 
             
              # Maximum number of bytes a varint can be
         | 
| 34 41 | 
             
              MAX_VARINT_SIZE = 5
         | 
| 42 | 
            +
              private_constant :MAX_VARINT_SIZE
         | 
| 43 | 
            +
             | 
| 35 44 | 
             
              # Default TCP port
         | 
| 36 45 | 
             
              DEFAULT_TCP_PORT = 25565
         | 
| 46 | 
            +
             | 
| 37 47 | 
             
              # Bedrock/Pocket Edition default UDP port
         | 
| 38 48 | 
             
              DEFAULT_BEDROCK_PORT = 19132
         | 
| 49 | 
            +
             | 
| 39 50 | 
             
              # Default TCP/UDP timeout in seconds
         | 
| 40 51 | 
             
              DEFAULT_TIMEOUT = 5
         | 
| 52 | 
            +
             | 
| 41 53 | 
             
              # Bedrock/Pocket Edition packet offset in bytes (1 + 8 + 8 + 16 + 2)
         | 
| 42 | 
            -
              # | 
| 43 | 
            -
              # | 
| 44 | 
            -
              # | 
| 45 | 
            -
              # | 
| 46 | 
            -
              # | 
| 54 | 
            +
              #   Unconnected pong (0x1C) = 1 byte
         | 
| 55 | 
            +
              #   Timestamp as a long = 8 bytes
         | 
| 56 | 
            +
              #   Server GUID as a long = 8 bytes
         | 
| 57 | 
            +
              #   Magic number = 16 bytes
         | 
| 58 | 
            +
              #   String ID length = 2 bytes
         | 
| 47 59 | 
             
              BEDROCK_PACKET_OFFSET = 35
         | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
              #  | 
| 60 | 
            +
              private_constant :BEDROCK_PACKET_OFFSET
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              # UT3/GS4 query handshake packet size in bytes (1 + 4 + 13)
         | 
| 63 | 
            +
              #   Handshake (0x09) = 1 byte
         | 
| 64 | 
            +
              #   Session ID = 4 bytes
         | 
| 65 | 
            +
              #   Challenge token = variable null-terminated string up to 13 bytes(?)
         | 
| 66 | 
            +
              QUERY_HANDSHAKE_SIZE = 18
         | 
| 67 | 
            +
              private_constant :QUERY_HANDSHAKE_SIZE
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              # UT3/GS4 query handshake packet offset for challenge token in bytes (1 + 4)
         | 
| 70 | 
            +
              #  Handshake (0x09) = 1 byte
         | 
| 71 | 
            +
              #  Session ID = 4 bytes
         | 
| 72 | 
            +
              QUERY_HANDSHAKE_OFFSET = 5
         | 
| 73 | 
            +
              private_constant :QUERY_HANDSHAKE_OFFSET
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              # UT3/GS4 query full stat packet offset in bytes (1 + 4 + 11)
         | 
| 76 | 
            +
              #   Stat (0x00) = 1 byte
         | 
| 77 | 
            +
              #   Session ID = 4 bytes
         | 
| 78 | 
            +
              #   Padding = 11 bytes
         | 
| 79 | 
            +
              QUERY_STAT_OFFSET = 16
         | 
| 80 | 
            +
              private_constant :QUERY_STAT_OFFSET
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              # These constants represent the result of a server request
         | 
| 51 83 | 
             
              module Retval
         | 
| 52 84 | 
             
                # The server ping completed successfully
         | 
| 53 85 | 
             
                SUCCESS = 0
         | 
| 86 | 
            +
             | 
| 54 87 | 
             
                # The server ping failed due to a connection error
         | 
| 55 88 | 
             
                CONNFAIL = -1
         | 
| 89 | 
            +
             | 
| 56 90 | 
             
                # The server ping failed due to a connection time out
         | 
| 57 91 | 
             
                TIMEOUT = -2
         | 
| 92 | 
            +
             | 
| 58 93 | 
             
                # The server ping failed for an unknown reason
         | 
| 59 94 | 
             
                UNKNOWN = -3
         | 
| 60 95 | 
             
              end
         | 
| 61 96 |  | 
| 62 | 
            -
               | 
| 63 | 
            -
              # Stores constants that represent the different kinds of server
         | 
| 64 | 
            -
              # list pings/requests that a Minecraft server might expect when
         | 
| 65 | 
            -
              # being polled for status information.
         | 
| 97 | 
            +
              # These constants represent the various protocols used when requesting server data
         | 
| 66 98 | 
             
              module Request
         | 
| 67 99 | 
             
                # Try everything
         | 
| 68 100 | 
             
                NONE = -1
         | 
| 101 | 
            +
             | 
| 69 102 | 
             
                # Server versions 1.8b to 1.3
         | 
| 70 103 | 
             
                BETA = 0
         | 
| 104 | 
            +
             | 
| 71 105 | 
             
                # Server versions 1.4 to 1.5
         | 
| 72 106 | 
             
                LEGACY = 1
         | 
| 107 | 
            +
             | 
| 73 108 | 
             
                # Server version 1.6
         | 
| 74 109 | 
             
                EXTENDED = 2
         | 
| 110 | 
            +
             | 
| 75 111 | 
             
                # Server versions 1.7 to latest
         | 
| 76 112 | 
             
                JSON = 3
         | 
| 113 | 
            +
             | 
| 77 114 | 
             
                # Bedrock/Pocket Edition
         | 
| 78 115 | 
             
                BEDROCK = 4
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                # Unreal Tournament 3/GameSpy 4 query
         | 
| 118 | 
            +
                QUERY = 5
         | 
| 79 119 | 
             
              end
         | 
| 80 120 |  | 
| 81 | 
            -
               | 
| 82 | 
            -
              #  | 
| 83 | 
            -
               | 
| 121 | 
            +
              # Instantiates a MineStat object and polls the specified server for information
         | 
| 122 | 
            +
              # @param address [String] Minecraft server address
         | 
| 123 | 
            +
              # @param port [Integer] Minecraft server TCP or UDP port
         | 
| 124 | 
            +
              # @param timeout [Integer] TCP/UDP timeout in seconds
         | 
| 125 | 
            +
              # @param request_type [Request] Protocol used to poll a Minecraft server
         | 
| 126 | 
            +
              # @param debug [Boolean] Enable or disable error output
         | 
| 127 | 
            +
              # @return [MineStat] A MineStat object
         | 
| 128 | 
            +
              # @example Simply connect to an address
         | 
| 129 | 
            +
              #   ms = MineStat.new("minecraft.frag.land")
         | 
| 130 | 
            +
              # @example Connect to an address on a certain TCP or UDP port
         | 
| 131 | 
            +
              #   ms = MineStat.new("minecraft.frag.land", 25567)
         | 
| 132 | 
            +
              # @example Same as above example and additionally includes a timeout in seconds
         | 
| 133 | 
            +
              #   ms = MineStat.new("minecraft.frag.land", 25567, 3)
         | 
| 134 | 
            +
              # @example Same as above example and additionally includes an explicit protocol to use
         | 
| 135 | 
            +
              #   ms = MineStat.new("minecraft.frag.land", 25567, 3, MineStat::Request::QUERY)
         | 
| 136 | 
            +
              # @example Connect to a Bedrock server and enable debug mode
         | 
| 137 | 
            +
              #   ms = MineStat.new("minecraft.frag.land", 19132, 3, MineStat::Request::BEDROCK, true)
         | 
| 138 | 
            +
              def initialize(address, port = DEFAULT_TCP_PORT, timeout = DEFAULT_TIMEOUT, request_type = Request::NONE, debug = false)
         | 
| 84 139 | 
             
                @address = address    # address of server
         | 
| 85 140 | 
             
                @port = port          # TCP/UDP port of server
         | 
| 86 141 | 
             
                @online               # online or offline?
         | 
| 87 142 | 
             
                @version              # server version
         | 
| 88 | 
            -
                @mode | 
| 143 | 
            +
                @mode                 # game mode (Bedrock/Pocket Edition only)
         | 
| 89 144 | 
             
                @motd                 # message of the day
         | 
| 90 145 | 
             
                @stripped_motd        # message of the day without formatting
         | 
| 91 146 | 
             
                @current_players      # current number of players online
         | 
| 92 147 | 
             
                @max_players          # maximum player capacity
         | 
| 148 | 
            +
                @player_list          # list of players (UT3/GS4 query only)
         | 
| 149 | 
            +
                @plugin_list          # list of plugins (UT3/GS4 query only)
         | 
| 93 150 | 
             
                @protocol             # protocol level
         | 
| 94 151 | 
             
                @json_data            # JSON data for 1.7 queries
         | 
| 95 152 | 
             
                @favicon_b64          # base64-encoded favicon possibly contained in JSON 1.7 responses
         | 
| @@ -100,9 +157,38 @@ class MineStat | |
| 100 157 | 
             
                @request_type         # protocol version
         | 
| 101 158 | 
             
                @connection_status    # status of connection ("Success", "Fail", "Timeout", or "Unknown")
         | 
| 102 159 | 
             
                @try_all = false      # try all protocols?
         | 
| 160 | 
            +
                @debug = debug        # debug mode
         | 
| 103 161 |  | 
| 104 162 | 
             
                @try_all = true if request_type == Request::NONE
         | 
| 163 | 
            +
                resolve_srv(address, port)
         | 
| 164 | 
            +
                set_connection_status(attempt_protocols(request_type))
         | 
| 165 | 
            +
              end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              # Attempts to resolve SRV records
         | 
| 168 | 
            +
              # @param address [String] Minecraft server address
         | 
| 169 | 
            +
              # @param port [Integer] Minecraft server TCP or UDP port
         | 
| 170 | 
            +
              # @return [Boolean] Whether or not SRV resolution was successful
         | 
| 171 | 
            +
              # @since 2.3.0
         | 
| 172 | 
            +
              def resolve_srv(address, port)
         | 
| 173 | 
            +
                begin
         | 
| 174 | 
            +
                  resolver = Resolv::DNS.new
         | 
| 175 | 
            +
                  res = resolver.getresource("_minecraft._tcp.#{@address}", Resolv::DNS::Resource::IN::SRV)
         | 
| 176 | 
            +
                  @address = res.target.to_s # SRV target
         | 
| 177 | 
            +
                  @port = res.port.to_i      # SRV port
         | 
| 178 | 
            +
                rescue => exception          # primarily catch Resolv::ResolvError and revert if unable to resolve SRV record(s)
         | 
| 179 | 
            +
                  $stderr.puts exception if @debug
         | 
| 180 | 
            +
                  @address = address
         | 
| 181 | 
            +
                  @port = port
         | 
| 182 | 
            +
                  return false
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
                return true
         | 
| 185 | 
            +
              end
         | 
| 186 | 
            +
              private :resolve_srv
         | 
| 105 187 |  | 
| 188 | 
            +
              # Attempts the use of various protocols
         | 
| 189 | 
            +
              # @param request_type [Request] Protocol used to poll a Minecraft server
         | 
| 190 | 
            +
              # @return [Retval] Return value
         | 
| 191 | 
            +
              def attempt_protocols(request_type)
         | 
| 106 192 | 
             
                case request_type
         | 
| 107 193 | 
             
                  when Request::BETA
         | 
| 108 194 | 
             
                    retval = beta_request()
         | 
| @@ -114,6 +200,8 @@ class MineStat | |
| 114 200 | 
             
                    retval = json_request()
         | 
| 115 201 | 
             
                  when Request::BEDROCK
         | 
| 116 202 | 
             
                    retval = bedrock_request()
         | 
| 203 | 
            +
                  when Request::QUERY
         | 
| 204 | 
            +
                    retval = query_request()
         | 
| 117 205 | 
             
                  else
         | 
| 118 206 | 
             
                    # Attempt various ping requests in a particular order. If the
         | 
| 119 207 | 
             
                    # connection fails, there is no reason to continue with subsequent
         | 
| @@ -138,17 +226,24 @@ class MineStat | |
| 138 226 | 
             
                    unless @online || retval == Retval::SUCCESS
         | 
| 139 227 | 
             
                      retval = bedrock_request()
         | 
| 140 228 | 
             
                    end
         | 
| 229 | 
            +
                    # UT3/GS4 query
         | 
| 230 | 
            +
                    unless @online || retval == Retval::SUCCESS
         | 
| 231 | 
            +
                      retval = query_request()
         | 
| 232 | 
            +
                    end
         | 
| 141 233 | 
             
                end
         | 
| 142 | 
            -
                 | 
| 234 | 
            +
                return retval
         | 
| 143 235 | 
             
              end
         | 
| 236 | 
            +
              private :attempt_protocols
         | 
| 144 237 |  | 
| 145 238 | 
             
              # Sets connection status
         | 
| 239 | 
            +
              # @param retval [Retval] Return value
         | 
| 146 240 | 
             
              def set_connection_status(retval)
         | 
| 147 | 
            -
                @connection_status = "Success" if retval == Retval::SUCCESS
         | 
| 241 | 
            +
                @connection_status = "Success" if @online || retval == Retval::SUCCESS
         | 
| 148 242 | 
             
                @connection_status = "Fail" if retval == Retval::CONNFAIL
         | 
| 149 243 | 
             
                @connection_status = "Timeout" if retval == Retval::TIMEOUT
         | 
| 150 244 | 
             
                @connection_status = "Unknown" if retval == Retval::UNKNOWN
         | 
| 151 245 | 
             
              end
         | 
| 246 | 
            +
              private :set_connection_status
         | 
| 152 247 |  | 
| 153 248 | 
             
              # Strips message of the day formatting characters
         | 
| 154 249 | 
             
              def strip_motd()
         | 
| @@ -168,13 +263,13 @@ class MineStat | |
| 168 263 | 
             
                @stripped_motd = @stripped_motd.force_encoding('UTF-8')
         | 
| 169 264 | 
             
                @stripped_motd = @stripped_motd.gsub(/§./, "")
         | 
| 170 265 | 
             
              end
         | 
| 266 | 
            +
              private :strip_motd
         | 
| 171 267 |  | 
| 172 | 
            -
              ##
         | 
| 173 268 | 
             
              # Establishes a connection to the Minecraft server
         | 
| 174 269 | 
             
              def connect()
         | 
| 175 270 | 
             
                begin
         | 
| 176 | 
            -
                  if @request_type == Request::BEDROCK || @request_type == "Bedrock/Pocket Edition"
         | 
| 177 | 
            -
                    @port = DEFAULT_BEDROCK_PORT if @port == DEFAULT_TCP_PORT && @try_all
         | 
| 271 | 
            +
                  if @request_type == Request::BEDROCK || @request_type == "Bedrock/Pocket Edition" || @request_type == "UT3/GS4 Query"
         | 
| 272 | 
            +
                    @port = DEFAULT_BEDROCK_PORT if @port == DEFAULT_TCP_PORT && @request_type != "UT3/GS4 Query" && @try_all
         | 
| 178 273 | 
             
                    start_time = Time.now
         | 
| 179 274 | 
             
                    @server = UDPSocket.new
         | 
| 180 275 | 
             
                    @server.connect(@address, @port)
         | 
| @@ -186,15 +281,18 @@ class MineStat | |
| 186 281 | 
             
                rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
         | 
| 187 282 | 
             
                  return Retval::CONNFAIL
         | 
| 188 283 | 
             
                rescue => exception
         | 
| 189 | 
            -
                  $stderr.puts exception
         | 
| 284 | 
            +
                  $stderr.puts exception if @debug
         | 
| 190 285 | 
             
                  return Retval::UNKNOWN
         | 
| 191 286 | 
             
                end
         | 
| 192 287 | 
             
                return Retval::SUCCESS
         | 
| 193 288 | 
             
              end
         | 
| 289 | 
            +
              private :connect
         | 
| 194 290 |  | 
| 195 | 
            -
              #  | 
| 196 | 
            -
               | 
| 291 | 
            +
              # Validates server response based on beginning of the packet
         | 
| 292 | 
            +
              # @return [String, Retval] Raw data received from a Minecraft server and the return value
         | 
| 293 | 
            +
              def check_response()
         | 
| 197 294 | 
             
                data = nil
         | 
| 295 | 
            +
                retval = nil
         | 
| 198 296 | 
             
                begin
         | 
| 199 297 | 
             
                  if @request_type == "Bedrock/Pocket Edition"
         | 
| 200 298 | 
             
                    if @server.recv(1, Socket::MSG_PEEK).unpack('C').first == 0x1C # unconnected pong packet
         | 
| @@ -203,7 +301,15 @@ class MineStat | |
| 203 301 | 
             
                      @server.close
         | 
| 204 302 | 
             
                    else
         | 
| 205 303 | 
             
                      @server.close
         | 
| 206 | 
            -
                       | 
| 304 | 
            +
                      retval = Retval::UNKNOWN
         | 
| 305 | 
            +
                    end
         | 
| 306 | 
            +
                  elsif @request_type == "UT3/GS4 Query"
         | 
| 307 | 
            +
                    if @server.recv(1, Socket::MSG_PEEK).unpack('C').first == 0x00 # stat packet
         | 
| 308 | 
            +
                      data = @server.recv(4096)[QUERY_STAT_OFFSET..-1]
         | 
| 309 | 
            +
                      @server.close
         | 
| 310 | 
            +
                    else
         | 
| 311 | 
            +
                      @server.close
         | 
| 312 | 
            +
                      retval = Retval::UNKNOWN
         | 
| 207 313 | 
             
                    end
         | 
| 208 314 | 
             
                  else # SLP
         | 
| 209 315 | 
             
                    if @server.read(1).unpack('C').first == 0xFF # kick packet (255)
         | 
| @@ -212,20 +318,31 @@ class MineStat | |
| 212 318 | 
             
                      @server.close
         | 
| 213 319 | 
             
                    else
         | 
| 214 320 | 
             
                      @server.close
         | 
| 215 | 
            -
                       | 
| 321 | 
            +
                      retval = Retval::UNKNOWN
         | 
| 216 322 | 
             
                    end
         | 
| 217 323 | 
             
                  end
         | 
| 218 | 
            -
                rescue
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                   | 
| 221 | 
            -
                  return Retval::UNKNOWN
         | 
| 324 | 
            +
                rescue => exception
         | 
| 325 | 
            +
                  $stderr.puts exception if @debug
         | 
| 326 | 
            +
                  return nil, Retval::UNKNOWN
         | 
| 222 327 | 
             
                end
         | 
| 328 | 
            +
                retval = Retval::UNKNOWN if data == nil || data.empty?
         | 
| 329 | 
            +
                return data, retval
         | 
| 330 | 
            +
              end
         | 
| 331 | 
            +
              private :check_response
         | 
| 223 332 |  | 
| 224 | 
            -
             | 
| 225 | 
            -
             | 
| 226 | 
            -
             | 
| 333 | 
            +
              # Populates object fields after retrieving data from a Minecraft server
         | 
| 334 | 
            +
              # @param delimiter [String] Delimiter used to split a string into an array
         | 
| 335 | 
            +
              # @param is_beta [Boolean] Whether or not the Minecraft server is using version 1.8b to 1.3
         | 
| 336 | 
            +
              def parse_data(delimiter, is_beta = false)
         | 
| 337 | 
            +
                data, retval = check_response()
         | 
| 338 | 
            +
                return retval if retval == Retval::UNKNOWN
         | 
| 227 339 |  | 
| 228 | 
            -
                server_info =  | 
| 340 | 
            +
                server_info = nil
         | 
| 341 | 
            +
                if @request_type == "UT3/GS4 Query"
         | 
| 342 | 
            +
                  server_info = data.split("\x00\x00\x01player_\x00\x00")
         | 
| 343 | 
            +
                else
         | 
| 344 | 
            +
                  server_info = data.split(delimiter)
         | 
| 345 | 
            +
                end
         | 
| 229 346 | 
             
                if is_beta
         | 
| 230 347 | 
             
                  if server_info != nil && server_info.length >= NUM_FIELDS_BETA
         | 
| 231 348 | 
             
                    @version = ">=1.8b/1.3" # since server does not return version, set it
         | 
| @@ -250,6 +367,25 @@ class MineStat | |
| 250 367 | 
             
                  else
         | 
| 251 368 | 
             
                    return Retval::UNKNOWN
         | 
| 252 369 | 
             
                  end
         | 
| 370 | 
            +
                elsif @request_type == "UT3/GS4 Query"
         | 
| 371 | 
            +
                  if server_info != nil
         | 
| 372 | 
            +
                    @player_list = server_info[1].split(delimiter) unless server_info[1].nil? || server_info[1].empty?
         | 
| 373 | 
            +
                    server_info = Hash[*server_info[0].split(delimiter).flatten(1)]
         | 
| 374 | 
            +
                    @version = server_info["version"]
         | 
| 375 | 
            +
                    @motd = server_info["hostname"]
         | 
| 376 | 
            +
                    strip_motd
         | 
| 377 | 
            +
                    @current_players = server_info["numplayers"].to_i
         | 
| 378 | 
            +
                    @max_players = server_info["maxplayers"].to_i
         | 
| 379 | 
            +
                    unless server_info["plugins"].nil? || server_info["plugins"].empty?
         | 
| 380 | 
            +
                      # Vanilla servers do not send a list of plugins.
         | 
| 381 | 
            +
                      # Bukkit and derivatives send plugins in the form: Paper on 1.19.3-R0.1-SNAPSHOT: Essentials 2.19.7; EssentialsChat 2.19.7
         | 
| 382 | 
            +
                      @plugin_list = server_info["plugins"].split(':')
         | 
| 383 | 
            +
                      @plugin_list = @plugin_list[1].split(';').collect(&:strip) if @plugin_list.size > 1
         | 
| 384 | 
            +
                    end
         | 
| 385 | 
            +
                    @online = true
         | 
| 386 | 
            +
                  else
         | 
| 387 | 
            +
                    return Retval::UNKNOWN
         | 
| 388 | 
            +
                  end
         | 
| 253 389 | 
             
                else # SLP
         | 
| 254 390 | 
             
                  if server_info != nil && server_info.length >= NUM_FIELDS
         | 
| 255 391 | 
             
                    # server_info[0] contains the section symbol and 1
         | 
| @@ -266,18 +402,22 @@ class MineStat | |
| 266 402 | 
             
                end
         | 
| 267 403 | 
             
                return Retval::SUCCESS
         | 
| 268 404 | 
             
              end
         | 
| 269 | 
            -
             | 
| 270 | 
            -
             | 
| 271 | 
            -
              # 1. | 
| 272 | 
            -
              #  | 
| 273 | 
            -
              # | 
| 274 | 
            -
              #    | 
| 275 | 
            -
              # | 
| 276 | 
            -
              # | 
| 277 | 
            -
              #  | 
| 278 | 
            -
              #    | 
| 279 | 
            -
              # | 
| 280 | 
            -
              # | 
| 405 | 
            +
              private :parse_data
         | 
| 406 | 
            +
             | 
| 407 | 
            +
              # 1.8b - 1.3 (SLP request)
         | 
| 408 | 
            +
              # @note
         | 
| 409 | 
            +
              #   1. Client sends 0xFE (server list ping)
         | 
| 410 | 
            +
              #   2. Server responds with:
         | 
| 411 | 
            +
              #     2a. 0xFF (kick packet)
         | 
| 412 | 
            +
              #     2b. data length
         | 
| 413 | 
            +
              #     2c. 3 fields delimited by \u00A7 (section symbol)
         | 
| 414 | 
            +
              #   The 3 fields, in order, are:
         | 
| 415 | 
            +
              #     * message of the day
         | 
| 416 | 
            +
              #     * current players
         | 
| 417 | 
            +
              #     * max players
         | 
| 418 | 
            +
              # @return [Retval] Return value
         | 
| 419 | 
            +
              # @since 0.2.1
         | 
| 420 | 
            +
              # @see https://wiki.vg/Server_List_Ping#Beta_1.8_to_1.3
         | 
| 281 421 | 
             
              def beta_request()
         | 
| 282 422 | 
             
                retval = nil
         | 
| 283 423 | 
             
                begin
         | 
| @@ -291,7 +431,7 @@ class MineStat | |
| 291 431 | 
             
                rescue Timeout::Error
         | 
| 292 432 | 
             
                  return Retval::TIMEOUT
         | 
| 293 433 | 
             
                rescue => exception
         | 
| 294 | 
            -
                  $stderr.puts exception
         | 
| 434 | 
            +
                  $stderr.puts exception if @debug
         | 
| 295 435 | 
             
                  return Retval::UNKNOWN
         | 
| 296 436 | 
             
                end
         | 
| 297 437 | 
             
                if retval == Retval::SUCCESS
         | 
| @@ -300,26 +440,29 @@ class MineStat | |
| 300 440 | 
             
                end
         | 
| 301 441 | 
             
                return retval
         | 
| 302 442 | 
             
              end
         | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
              # 1.4 and 1.5  | 
| 306 | 
            -
              #  | 
| 307 | 
            -
              #    | 
| 308 | 
            -
              # | 
| 309 | 
            -
              # | 
| 310 | 
            -
              #    | 
| 311 | 
            -
              # | 
| 312 | 
            -
              # | 
| 313 | 
            -
              #  | 
| 314 | 
            -
              #    | 
| 315 | 
            -
              # | 
| 316 | 
            -
              # | 
| 317 | 
            -
              # | 
| 318 | 
            -
              # | 
| 319 | 
            -
              # | 
| 443 | 
            +
              private :beta_request
         | 
| 444 | 
            +
             | 
| 445 | 
            +
              # 1.4 and 1.5 (SLP request)
         | 
| 446 | 
            +
              # @note
         | 
| 447 | 
            +
              #   1. Client sends:
         | 
| 448 | 
            +
              #     1a. 0xFE (server list ping)
         | 
| 449 | 
            +
              #     1b. 0x01 (server list ping payload)
         | 
| 450 | 
            +
              #   2. Server responds with:
         | 
| 451 | 
            +
              #     2a. 0xFF (kick packet)
         | 
| 452 | 
            +
              #     2b. data length
         | 
| 453 | 
            +
              #     2c. 6 fields delimited by 0x00 (null)
         | 
| 454 | 
            +
              #   The 6 fields, in order, are:
         | 
| 455 | 
            +
              #     * the section symbol and 1
         | 
| 456 | 
            +
              #     * protocol version
         | 
| 457 | 
            +
              #     * server version
         | 
| 458 | 
            +
              #     * message of the day
         | 
| 459 | 
            +
              #     * current players
         | 
| 460 | 
            +
              #     * max players
         | 
| 320 461 | 
             
              #
         | 
| 321 | 
            -
              # | 
| 322 | 
            -
              # | 
| 462 | 
            +
              #   The protocol version corresponds with the server version and can be the
         | 
| 463 | 
            +
              #   same for different server versions.
         | 
| 464 | 
            +
              # @return [Retval] Return value
         | 
| 465 | 
            +
              # @see https://wiki.vg/Server_List_Ping#1.4_to_1.5
         | 
| 323 466 | 
             
              def legacy_request()
         | 
| 324 467 | 
             
                retval = nil
         | 
| 325 468 | 
             
                begin
         | 
| @@ -333,7 +476,7 @@ class MineStat | |
| 333 476 | 
             
                rescue Timeout::Error
         | 
| 334 477 | 
             
                  return Retval::TIMEOUT
         | 
| 335 478 | 
             
                rescue => exception
         | 
| 336 | 
            -
                  $stderr.puts exception
         | 
| 479 | 
            +
                  $stderr.puts exception if @debug
         | 
| 337 480 | 
             
                  return Retval::UNKNOWN
         | 
| 338 481 | 
             
                end
         | 
| 339 482 | 
             
                if retval == Retval::SUCCESS
         | 
| @@ -342,34 +485,38 @@ class MineStat | |
| 342 485 | 
             
                end
         | 
| 343 486 | 
             
                return retval
         | 
| 344 487 | 
             
              end
         | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
              # 1.6  | 
| 348 | 
            -
              #  | 
| 349 | 
            -
              #    | 
| 350 | 
            -
              # | 
| 351 | 
            -
              # | 
| 352 | 
            -
              # | 
| 353 | 
            -
              # | 
| 354 | 
            -
              # | 
| 355 | 
            -
              # | 
| 356 | 
            -
              # | 
| 357 | 
            -
              # | 
| 358 | 
            -
              # | 
| 359 | 
            -
              # | 
| 360 | 
            -
              #    | 
| 361 | 
            -
              # | 
| 362 | 
            -
              # | 
| 363 | 
            -
              #  | 
| 364 | 
            -
              #    | 
| 365 | 
            -
              # | 
| 366 | 
            -
              # | 
| 367 | 
            -
              # | 
| 368 | 
            -
              # | 
| 369 | 
            -
              # | 
| 488 | 
            +
              private :legacy_request
         | 
| 489 | 
            +
             | 
| 490 | 
            +
              # 1.6 (SLP request)
         | 
| 491 | 
            +
              # @note
         | 
| 492 | 
            +
              #   1. Client sends:
         | 
| 493 | 
            +
              #     1a. 0xFE (server list ping)
         | 
| 494 | 
            +
              #     1b. 0x01 (server list ping payload)
         | 
| 495 | 
            +
              #     1c. 0xFA (plugin message)
         | 
| 496 | 
            +
              #     1d. 0x00 0x0B (11 which is the length of "MC|PingHost")
         | 
| 497 | 
            +
              #     1e. "MC|PingHost" encoded as a UTF-16BE string
         | 
| 498 | 
            +
              #     1f. length of remaining data as a short: remote address (encoded as UTF-16BE) + 7
         | 
| 499 | 
            +
              #     1g. arbitrary 1.6 protocol version (0x4E for example for 78)
         | 
| 500 | 
            +
              #     1h. length of remote address as a short
         | 
| 501 | 
            +
              #     1i. remote address encoded as a UTF-16BE string
         | 
| 502 | 
            +
              #     1j. remote port as an int
         | 
| 503 | 
            +
              #   2. Server responds with:
         | 
| 504 | 
            +
              #     2a. 0xFF (kick packet)
         | 
| 505 | 
            +
              #     2b. data length
         | 
| 506 | 
            +
              #     2c. 6 fields delimited by 0x00 (null)
         | 
| 507 | 
            +
              #   The 6 fields, in order, are:
         | 
| 508 | 
            +
              #     * the section symbol and 1
         | 
| 509 | 
            +
              #     * protocol version
         | 
| 510 | 
            +
              #     * server version
         | 
| 511 | 
            +
              #     * message of the day
         | 
| 512 | 
            +
              #     * current players
         | 
| 513 | 
            +
              #     * max players
         | 
| 370 514 | 
             
              #
         | 
| 371 515 | 
             
              # The protocol version corresponds with the server version and can be the
         | 
| 372 516 | 
             
              # same for different server versions.
         | 
| 517 | 
            +
              # @return [Retval] Return value
         | 
| 518 | 
            +
              # @since 0.2.0
         | 
| 519 | 
            +
              # @see https://wiki.vg/Server_List_Ping#1.6
         | 
| 373 520 | 
             
              def extended_legacy_request()
         | 
| 374 521 | 
             
                retval = nil
         | 
| 375 522 | 
             
                begin
         | 
| @@ -390,7 +537,7 @@ class MineStat | |
| 390 537 | 
             
                rescue Timeout::Error
         | 
| 391 538 | 
             
                  return Retval::TIMEOUT
         | 
| 392 539 | 
             
                rescue => exception
         | 
| 393 | 
            -
                  $stderr.puts exception
         | 
| 540 | 
            +
                  $stderr.puts exception if @debug
         | 
| 394 541 | 
             
                  return Retval::UNKNOWN
         | 
| 395 542 | 
             
                end
         | 
| 396 543 | 
             
                if retval == Retval::SUCCESS
         | 
| @@ -399,23 +546,27 @@ class MineStat | |
| 399 546 | 
             
                end
         | 
| 400 547 | 
             
                return retval
         | 
| 401 548 | 
             
              end
         | 
| 402 | 
            -
             | 
| 403 | 
            -
             | 
| 404 | 
            -
              # 1.7  | 
| 405 | 
            -
              #  | 
| 406 | 
            -
              #    | 
| 407 | 
            -
              # | 
| 408 | 
            -
              #  | 
| 409 | 
            -
              # | 
| 410 | 
            -
              # | 
| 411 | 
            -
              # | 
| 412 | 
            -
              # | 
| 413 | 
            -
              # | 
| 414 | 
            -
              #    | 
| 415 | 
            -
              #  | 
| 416 | 
            -
              #  | 
| 417 | 
            -
              # | 
| 418 | 
            -
              # | 
| 549 | 
            +
              private :extended_legacy_request
         | 
| 550 | 
            +
             | 
| 551 | 
            +
              # >=1.7 (SLP request)
         | 
| 552 | 
            +
              # @note
         | 
| 553 | 
            +
              #   1. Client sends:
         | 
| 554 | 
            +
              #     1a. 0x00 (handshake packet containing the fields specified below)
         | 
| 555 | 
            +
              #     1b. 0x00 (request)
         | 
| 556 | 
            +
              #   The handshake packet contains the following fields respectively:
         | 
| 557 | 
            +
              #       1. protocol version as a varint (0x00 suffices)
         | 
| 558 | 
            +
              #       2. remote address as a string
         | 
| 559 | 
            +
              #       3. remote port as an unsigned short
         | 
| 560 | 
            +
              #       4. state as a varint (should be 1 for status)
         | 
| 561 | 
            +
              #   2. Server responds with:
         | 
| 562 | 
            +
              #     2a. 0x00 (JSON response)
         | 
| 563 | 
            +
              #   An example JSON string contains:
         | 
| 564 | 
            +
              #     {'players': {'max': 20, 'online': 0},
         | 
| 565 | 
            +
              #     'version': {'protocol': 404, 'name': '1.13.2'},
         | 
| 566 | 
            +
              #     'description': {'text': 'A Minecraft Server'}}
         | 
| 567 | 
            +
              # @return [Retval] Return value
         | 
| 568 | 
            +
              # @since 0.3.0
         | 
| 569 | 
            +
              # @see https://wiki.vg/Server_List_Ping#Current_.281.7.2B.29
         | 
| 419 570 | 
             
              def json_request()
         | 
| 420 571 | 
             
                retval = nil
         | 
| 421 572 | 
             
                begin
         | 
| @@ -464,7 +615,7 @@ class MineStat | |
| 464 615 | 
             
                rescue JSON::ParserError
         | 
| 465 616 | 
             
                  return Retval::UNKNOWN
         | 
| 466 617 | 
             
                rescue => exception
         | 
| 467 | 
            -
                  $stderr.puts exception
         | 
| 618 | 
            +
                  $stderr.puts exception if @debug
         | 
| 468 619 | 
             
                  return Retval::UNKNOWN
         | 
| 469 620 | 
             
                end
         | 
| 470 621 | 
             
                if retval == Retval::SUCCESS
         | 
| @@ -473,8 +624,11 @@ class MineStat | |
| 473 624 | 
             
                end
         | 
| 474 625 | 
             
                return retval
         | 
| 475 626 | 
             
              end
         | 
| 627 | 
            +
              private :json_request
         | 
| 476 628 |  | 
| 477 629 | 
             
              # Reads JSON data from the socket
         | 
| 630 | 
            +
              # @param json_len [Integer] Length of the JSON data received from the Minecraft server
         | 
| 631 | 
            +
              # @return [String] JSON data received from the Mincraft server
         | 
| 478 632 | 
             
              def recv_json(json_len)
         | 
| 479 633 | 
             
                json_data = ""
         | 
| 480 634 | 
             
                begin
         | 
| @@ -486,12 +640,15 @@ class MineStat | |
| 486 640 | 
             
                    break if json_data.length >= json_len
         | 
| 487 641 | 
             
                  end
         | 
| 488 642 | 
             
                rescue => exception
         | 
| 489 | 
            -
                  $stderr.puts exception
         | 
| 643 | 
            +
                  $stderr.puts exception if @debug
         | 
| 490 644 | 
             
                end
         | 
| 491 645 | 
             
                return json_data
         | 
| 492 646 | 
             
              end
         | 
| 647 | 
            +
              private :recv_json
         | 
| 493 648 |  | 
| 494 | 
            -
              #  | 
| 649 | 
            +
              # Decodes the value of a varint type
         | 
| 650 | 
            +
              # @return [Integer] Value decoded from a varint type
         | 
| 651 | 
            +
              # @see https://en.wikipedia.org/wiki/LEB128
         | 
| 495 652 | 
             
              def unpack_varint()
         | 
| 496 653 | 
             
                vint = 0
         | 
| 497 654 | 
             
                i = 0
         | 
| @@ -505,34 +662,38 @@ class MineStat | |
| 505 662 | 
             
                end
         | 
| 506 663 | 
             
                return vint
         | 
| 507 664 | 
             
              end
         | 
| 508 | 
            -
             | 
| 509 | 
            -
             | 
| 510 | 
            -
              # Bedrock/Pocket Edition  | 
| 511 | 
            -
              #  | 
| 512 | 
            -
              #    | 
| 513 | 
            -
              # | 
| 514 | 
            -
              # | 
| 515 | 
            -
              # | 
| 516 | 
            -
              # | 
| 517 | 
            -
              #    | 
| 518 | 
            -
              # | 
| 519 | 
            -
              # | 
| 520 | 
            -
              # | 
| 521 | 
            -
              # | 
| 522 | 
            -
              # | 
| 523 | 
            -
              #  | 
| 524 | 
            -
              #    | 
| 525 | 
            -
              # | 
| 526 | 
            -
              # | 
| 527 | 
            -
              # | 
| 528 | 
            -
              # | 
| 529 | 
            -
              # | 
| 530 | 
            -
              # | 
| 531 | 
            -
              # | 
| 532 | 
            -
              # | 
| 533 | 
            -
              # | 
| 534 | 
            -
              # | 
| 535 | 
            -
              # | 
| 665 | 
            +
              private :unpack_varint
         | 
| 666 | 
            +
             | 
| 667 | 
            +
              # Bedrock/Pocket Edition (unconnected ping request)
         | 
| 668 | 
            +
              # @note
         | 
| 669 | 
            +
              #   1. Client sends:
         | 
| 670 | 
            +
              #     1a. 0x01 (unconnected ping packet containing the fields specified below)
         | 
| 671 | 
            +
              #     1b. current time as a long
         | 
| 672 | 
            +
              #     1c. magic number
         | 
| 673 | 
            +
              #     1d. client GUID as a long
         | 
| 674 | 
            +
              #   2. Server responds with:
         | 
| 675 | 
            +
              #     2a. 0x1c (unconnected pong packet containing the follow fields)
         | 
| 676 | 
            +
              #     2b. current time as a long
         | 
| 677 | 
            +
              #     2c. server GUID as a long
         | 
| 678 | 
            +
              #     2d. 16-bit magic number
         | 
| 679 | 
            +
              #     2e. server ID string length
         | 
| 680 | 
            +
              #     2f. server ID as a string
         | 
| 681 | 
            +
              #   The fields from the pong response, in order, are:
         | 
| 682 | 
            +
              #     * edition
         | 
| 683 | 
            +
              #     * MotD line 1
         | 
| 684 | 
            +
              #     * protocol version
         | 
| 685 | 
            +
              #     * version name
         | 
| 686 | 
            +
              #     * current player count
         | 
| 687 | 
            +
              #     * maximum player count
         | 
| 688 | 
            +
              #     * unique server ID
         | 
| 689 | 
            +
              #     * MotD line 2
         | 
| 690 | 
            +
              #     * game mode as a string
         | 
| 691 | 
            +
              #     * game mode as a numeric
         | 
| 692 | 
            +
              #     * IPv4 port number
         | 
| 693 | 
            +
              #     * IPv6 port number
         | 
| 694 | 
            +
              # @return [Retval] Return value
         | 
| 695 | 
            +
              # @since 2.2.0
         | 
| 696 | 
            +
              # @see https://wiki.vg/Raknet_Protocol#Unconnected_Ping
         | 
| 536 697 | 
             
              def bedrock_request()
         | 
| 537 698 | 
             
                retval = nil
         | 
| 538 699 | 
             
                begin
         | 
| @@ -552,7 +713,7 @@ class MineStat | |
| 552 713 | 
             
                rescue Timeout::Error
         | 
| 553 714 | 
             
                  return Retval::TIMEOUT
         | 
| 554 715 | 
             
                rescue => exception
         | 
| 555 | 
            -
                  $stderr.puts exception
         | 
| 716 | 
            +
                  $stderr.puts exception if @debug
         | 
| 556 717 | 
             
                  return Retval::UNKNOWN
         | 
| 557 718 | 
             
                end
         | 
| 558 719 | 
             
                if retval == Retval::SUCCESS
         | 
| @@ -560,62 +721,155 @@ class MineStat | |
| 560 721 | 
             
                end
         | 
| 561 722 | 
             
                return retval
         | 
| 562 723 | 
             
              end
         | 
| 724 | 
            +
              private :bedrock_request
         | 
| 725 | 
            +
             | 
| 726 | 
            +
              # Unreal Tournament 3/GameSpy 4 (UT3/GS4) query protocol
         | 
| 727 | 
            +
              # @note
         | 
| 728 | 
            +
              #   1. Client sends:
         | 
| 729 | 
            +
              #     1a. 0xFE 0xFD (query identifier)
         | 
| 730 | 
            +
              #     1b. 0x09 (handshake)
         | 
| 731 | 
            +
              #     1c. arbitrary session ID (4 bytes)
         | 
| 732 | 
            +
              #   2. Server responds with:
         | 
| 733 | 
            +
              #     2a. 0x09 (handshake)
         | 
| 734 | 
            +
              #     2b. session ID (4 bytes)
         | 
| 735 | 
            +
              #     2c. challenge token (variable null-terminated string)
         | 
| 736 | 
            +
              #   3. Client sends:
         | 
| 737 | 
            +
              #     3a. 0xFE 0xFD (query identifier)
         | 
| 738 | 
            +
              #     3b. 0x00 (stat)
         | 
| 739 | 
            +
              #     3c. arbitrary session ID (4 bytes)
         | 
| 740 | 
            +
              #     3d. challenge token (32-bit integer in network byte order)
         | 
| 741 | 
            +
              #     3e. padding (4 bytes -- 0x00 0x00 0x00 0x00); omit padding for basic stat (which does not supply the version)
         | 
| 742 | 
            +
              #   4. Server responds with:
         | 
| 743 | 
            +
              #     4a. 0x00 (stat)
         | 
| 744 | 
            +
              #     4b. session ID (4 bytes)
         | 
| 745 | 
            +
              #     4c. padding (11 bytes)
         | 
| 746 | 
            +
              #     4e. key/value pairs of multiple null-terminated strings containing the fields below:
         | 
| 747 | 
            +
              #         hostname, game type, game ID, version, plugin list, map, current players, max players, port, address
         | 
| 748 | 
            +
              #     4f. padding (10 bytes)
         | 
| 749 | 
            +
              #     4g. list of null-terminated strings containing player names
         | 
| 750 | 
            +
              # @return [Retval] Return value
         | 
| 751 | 
            +
              # @since 3.0.0
         | 
| 752 | 
            +
              # @see https://wiki.vg/Query
         | 
| 753 | 
            +
              def query_request()
         | 
| 754 | 
            +
                retval = nil
         | 
| 755 | 
            +
                begin
         | 
| 756 | 
            +
                  Timeout::timeout(@timeout) do
         | 
| 757 | 
            +
                    @request_type = "UT3/GS4 Query"
         | 
| 758 | 
            +
                    retval = connect()
         | 
| 759 | 
            +
                    return retval unless retval == Retval::SUCCESS
         | 
| 760 | 
            +
                    payload = "\xFE\xFD\x09\x0B\x03\x03\x0F"
         | 
| 761 | 
            +
                    @server.write(payload)
         | 
| 762 | 
            +
                    @server.flush
         | 
| 763 | 
            +
                    if @server.recv(1, Socket::MSG_PEEK).unpack('C').first == 0x09 # query handshake packet
         | 
| 764 | 
            +
                      # Session ID generated by the server is not used -- use a static session ID instead such as 0x0B 0x03 0x03 0x0F. 
         | 
| 765 | 
            +
                      #session_id = @server.recv(QUERY_HANDSHAKE_OFFSET, Socket::MSG_PEEK)[1..-1].unpack('l>')
         | 
| 766 | 
            +
                      challenge_token = @server.recv(QUERY_HANDSHAKE_SIZE)[QUERY_HANDSHAKE_OFFSET..-1]
         | 
| 767 | 
            +
                      payload = "\xFE\xFD\x00\x0B\x03\x03\x0F".force_encoding('ASCII-8BIT')
         | 
| 768 | 
            +
                      # Use the full stat below by stripping the null terminator from the challenge token and padding the end
         | 
| 769 | 
            +
                      # of the payload  with "\x00\x00\x00\x00". The basic stat response does not include the server version.
         | 
| 770 | 
            +
                      payload += [challenge_token.rstrip.to_i].pack('l>').force_encoding('ASCII-8BIT')
         | 
| 771 | 
            +
                      payload += "\x00\x00\x00\x00".force_encoding('ASCII-8BIT')
         | 
| 772 | 
            +
                      @server.write(payload)
         | 
| 773 | 
            +
                      @server.flush          
         | 
| 774 | 
            +
                    else
         | 
| 775 | 
            +
                      return Retval::UNKNOWN
         | 
| 776 | 
            +
                    end
         | 
| 777 | 
            +
                    retval = parse_data("\x00") # null
         | 
| 778 | 
            +
                  end
         | 
| 779 | 
            +
                rescue Timeout::Error
         | 
| 780 | 
            +
                  return Retval::TIMEOUT
         | 
| 781 | 
            +
                rescue => exception
         | 
| 782 | 
            +
                  $stderr.puts exception if @debug
         | 
| 783 | 
            +
                  return Retval::UNKNOWN
         | 
| 784 | 
            +
                end
         | 
| 785 | 
            +
                if retval == Retval::SUCCESS
         | 
| 786 | 
            +
                  set_connection_status(retval)
         | 
| 787 | 
            +
                end
         | 
| 788 | 
            +
                return retval
         | 
| 789 | 
            +
              end
         | 
| 790 | 
            +
              private :query_request
         | 
| 563 791 |  | 
| 564 | 
            -
              #  | 
| 792 | 
            +
              # Address (hostname or IP address) of the Minecraft server
         | 
| 565 793 | 
             
              attr_reader :address
         | 
| 566 794 |  | 
| 567 | 
            -
              #  | 
| 795 | 
            +
              # Port (TCP or UDP) of the Minecraft server
         | 
| 568 796 | 
             
              attr_reader :port
         | 
| 569 797 |  | 
| 570 | 
            -
              #  | 
| 798 | 
            +
              # Whether or not the Minecraft server is online
         | 
| 571 799 | 
             
              attr_reader :online
         | 
| 572 800 |  | 
| 573 | 
            -
              #  | 
| 801 | 
            +
              # Minecraft server version
         | 
| 574 802 | 
             
              attr_reader :version
         | 
| 575 803 |  | 
| 576 | 
            -
              #  | 
| 804 | 
            +
              # Game mode
         | 
| 805 | 
            +
              # @note Bedrock/Pocket Edition only
         | 
| 806 | 
            +
              # @since 2.2.0
         | 
| 577 807 | 
             
              attr_reader :mode
         | 
| 578 808 |  | 
| 579 | 
            -
              #  | 
| 580 | 
            -
              #
         | 
| 581 | 
            -
              #  | 
| 809 | 
            +
              # Full message of the day (MotD)
         | 
| 810 | 
            +
              # @note If only the plain text MotD is relevant, use {#stripped_motd}
         | 
| 811 | 
            +
              # @see #stripped_motd
         | 
| 582 812 | 
             
              attr_reader :motd
         | 
| 583 813 |  | 
| 584 | 
            -
              #  | 
| 814 | 
            +
              # Plain text contained within the message of the day (MotD)
         | 
| 815 | 
            +
              # @note If the full MotD is desired, use {#motd}
         | 
| 816 | 
            +
              # @see #motd
         | 
| 585 817 | 
             
              attr_reader :stripped_motd
         | 
| 586 818 |  | 
| 587 | 
            -
              #  | 
| 819 | 
            +
              # Current player count
         | 
| 588 820 | 
             
              attr_reader :current_players
         | 
| 589 821 |  | 
| 590 | 
            -
              #  | 
| 822 | 
            +
              # Maximum player limit
         | 
| 591 823 | 
             
              attr_reader :max_players
         | 
| 592 824 |  | 
| 593 | 
            -
              #  | 
| 594 | 
            -
              #
         | 
| 595 | 
            -
              #  | 
| 596 | 
            -
               | 
| 597 | 
            -
             | 
| 825 | 
            +
              # List of players
         | 
| 826 | 
            +
              # @note UT3/GS4 query only
         | 
| 827 | 
            +
              # @since 3.0.0
         | 
| 828 | 
            +
              attr_reader :player_list
         | 
| 829 | 
            +
             | 
| 830 | 
            +
              # List of plugins
         | 
| 831 | 
            +
              # @note UT3/GS4 query only
         | 
| 832 | 
            +
              # @since 3.0.0
         | 
| 833 | 
            +
              attr_reader :plugin_list
         | 
| 834 | 
            +
             | 
| 835 | 
            +
              # Protocol level
         | 
| 836 | 
            +
              # @note This is arbitrary and varies by Minecraft version (may also be shared by multiple Minecraft versions)
         | 
| 598 837 | 
             
              attr_reader :protocol
         | 
| 599 838 |  | 
| 600 | 
            -
              #  | 
| 601 | 
            -
              #  | 
| 839 | 
            +
              # Complete JSON response data
         | 
| 840 | 
            +
              # @note Received using SLP 1.7 (JSON) queries
         | 
| 841 | 
            +
              # @since 0.3.0
         | 
| 602 842 | 
             
              attr_reader :json_data
         | 
| 603 843 |  | 
| 604 | 
            -
              #  | 
| 844 | 
            +
              # Base64-encoded favicon
         | 
| 845 | 
            +
              # @note Received using SLP 1.7 (JSON) queries
         | 
| 846 | 
            +
              # @since 2.2.2
         | 
| 605 847 | 
             
              attr_reader :favicon_b64
         | 
| 606 848 |  | 
| 607 | 
            -
              #  | 
| 849 | 
            +
              # Decoded favicon
         | 
| 850 | 
            +
              # @note Received using SLP 1.7 (JSON) queries
         | 
| 851 | 
            +
              # @since 2.2.2
         | 
| 608 852 | 
             
              attr_reader :favicon
         | 
| 609 853 |  | 
| 610 | 
            -
              #  | 
| 854 | 
            +
              # Ping time to the server in milliseconds (ms)
         | 
| 855 | 
            +
              # @since 0.2.1
         | 
| 611 856 | 
             
              attr_reader :latency
         | 
| 612 857 |  | 
| 613 | 
            -
              #  | 
| 858 | 
            +
              # TCP/UDP timeout in seconds
         | 
| 859 | 
            +
              # @since 0.1.2
         | 
| 860 | 
            +
              attr_accessor :timeout
         | 
| 861 | 
            +
             | 
| 862 | 
            +
              # Protocol used to request data from a Minecraft server
         | 
| 614 863 | 
             
              attr_reader :request_type
         | 
| 615 864 |  | 
| 616 | 
            -
              #  | 
| 865 | 
            +
              # Connection status
         | 
| 866 | 
            +
              # @since 2.2.2
         | 
| 617 867 | 
             
              attr_reader :connection_status
         | 
| 618 868 |  | 
| 619 | 
            -
              #  | 
| 869 | 
            +
              # Whether or not all protocols should be attempted
         | 
| 620 870 | 
             
              attr_reader :try_all
         | 
| 871 | 
            +
             | 
| 872 | 
            +
              # Whether or not debug mode is enabled
         | 
| 873 | 
            +
              # @since 3.0.0
         | 
| 874 | 
            +
              attr_reader :debug
         | 
| 621 875 | 
             
            end
         |