minestat 0.2.1 → 2.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/minestat.rb +349 -149
  3. metadata +7 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20bcda7c193a48fa96c641630b9a9c732c7b0042d711732c8c6a60774ea425e5
4
- data.tar.gz: 39fe195c29631131ee3003d0931658556395efa64c1513cbaa28acde0240fea6
3
+ metadata.gz: b2824d765e8d40dd3a1087567b27a9a04c9df93427b344c8e47346a0a778f5be
4
+ data.tar.gz: 5974801cb768bc9a67cd789a0296b569c1f9b14442d7124135c3ddbd8d41886b
5
5
  SHA512:
6
- metadata.gz: 59a25b2226f196216bffe13ba50f3ae4716c9110034bf1db23064fa0ea2b80436fd3e42a996a46a71e7a7041da0edd52767a0df92285b15a54a325545ee4d8f3
7
- data.tar.gz: 8a18ab5dfecee0d7fc7adeb7b6e63c16165d499db8f679a27ad63833e082fffe52f040b482009b0941750f7eee819bb59ad3f24d8c924d2aef9b339488b83176
6
+ metadata.gz: fc7bb79f048f5d280405638d416cb74ef8e0da6873074bd6e1b70c8176c913fc5fa59ffb92aaa3673f972225851b533964402cfc76160d1d5caec4bf207c43a2
7
+ data.tar.gz: 885fb2073958d8a95f121ed7a45e230987520dd0878744650bfe6ec45082a75a1e5dc9d512e76aff6dad965b3a36e493a76e9e5e6bd085bde14d2dd562eb33e6
data/lib/minestat.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # minestat.rb - A Minecraft server status checker
2
- # Copyright (C) 2014 Lloyd Dilley
2
+ # Copyright (C) 2014-2021 Lloyd Dilley
3
3
  # http://www.dilley.me/
4
4
  #
5
5
  # This program is free software; you can redistribute it and/or modify
@@ -16,106 +16,225 @@
16
16
  # with this program; if not, write to the Free Software Foundation, Inc.,
17
17
  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
 
19
+ require 'json'
19
20
  require 'socket'
20
21
  require 'timeout'
21
22
 
23
+ ##
24
+ # Provides a ruby interface for polling Minecraft server status.
22
25
  class MineStat
23
- NUM_FIELDS = 6 # number of values expected from server
24
- NUM_FIELDS_BETA = 3 # number of values expected from a 1.8b/1.3 server
25
- DEFAULT_TIMEOUT = 5 # default TCP timeout in seconds
26
+ # MineStat version
27
+ VERSION = "2.1.1"
28
+ # Number of values expected from server
29
+ NUM_FIELDS = 6
30
+ # Number of values expected from a 1.8b/1.3 server
31
+ NUM_FIELDS_BETA = 3
32
+ # Maximum number of bytes a varint can be
33
+ MAX_VARINT_SIZE = 5
34
+ # Default TCP port
35
+ DEFAULT_PORT = 25565
36
+ # Default TCP timeout in seconds
37
+ DEFAULT_TIMEOUT = 5
26
38
 
39
+ ##
40
+ # Stores constants that represent the results of a server ping
27
41
  module Retval
42
+ # The server ping completed successfully
28
43
  SUCCESS = 0
44
+ # The server ping failed due to a connection error
29
45
  CONNFAIL = -1
46
+ # The server ping failed due to a connection time out
30
47
  TIMEOUT = -2
48
+ # The server ping failed for an unknown reason
31
49
  UNKNOWN = -3
32
50
  end
33
51
 
34
- def initialize(address, port, timeout = DEFAULT_TIMEOUT)
35
- @address = address
36
- @port = port
52
+ ##
53
+ # Stores constants that represent the different kinds of server
54
+ # list pings/requests that a Minecraft server might expect when
55
+ # being polled for status information.
56
+ module Request
57
+ # Try everything
58
+ NONE = -1
59
+ # Server versions 1.8b to 1.3
60
+ BETA = 0
61
+ # Server versions 1.4 to 1.5
62
+ LEGACY = 1
63
+ # Server version 1.6
64
+ EXTENDED = 2
65
+ # Server versions 1.7 to latest
66
+ JSON = 3
67
+ end
68
+
69
+ ##
70
+ # Instantiate an instance of MineStat and poll the specified server for information
71
+ def initialize(address, port = DEFAULT_PORT, timeout = DEFAULT_TIMEOUT, request_type = Request::NONE)
72
+ @address = address # address of server
73
+ @port = port # TCP port of server
37
74
  @online # online or offline?
38
75
  @version # server version
39
76
  @motd # message of the day
77
+ @stripped_motd # message of the day without formatting
40
78
  @current_players # current number of players online
41
79
  @max_players # maximum player capacity
80
+ @protocol # protocol level
81
+ @json_data # JSON data for 1.7 queries
42
82
  @latency # ping time to server in milliseconds
83
+ @timeout = timeout # TCP timeout
84
+ @server # server socket
85
+ @request_type # SLP protocol version
43
86
 
44
- # Try the newest protocol first and work down. If the query succeeds or the
45
- # connection fails, there is no reason to continue with subsequent queries.
46
- # Attempts should continue in the event of a timeout however since it may
47
- # be due to an issue during the handshake.
48
- # Note: Newer server versions may still respond to older ping query types.
49
- # For example, 1.13.2 responds to 1.4/1.5 queries, but not 1.6 queries.
50
- # 1.7
51
- retval = json_query(address, port, timeout)
52
- # 1.6
53
- unless retval == Retval::SUCCESS || retval == Retval::CONNFAIL
54
- retval = new_query(address, port, timeout)
87
+ case request_type
88
+ when Request::BETA
89
+ retval = beta_request()
90
+ when Request::LEGACY
91
+ retval = legacy_request()
92
+ when Request::EXTENDED
93
+ retval = extended_legacy_request()
94
+ when Request::JSON
95
+ retval = json_request()
96
+ else
97
+ # Attempt various SLP ping requests in a particular order. If the request
98
+ # succeeds or the connection fails, there is no reason to continue with
99
+ # subsequent requests. Attempts should continue in the event of a timeout
100
+ # however since it may be due to an issue during the handshake.
101
+ # Note: Newer server versions may still respond to older SLP requests.
102
+ # For example, 1.13.2 responds to 1.4/1.5 queries, but not 1.6 queries.
103
+ # SLP 1.4/1.5
104
+ retval = legacy_request()
105
+ # SLP 1.8b/1.3
106
+ unless retval == Retval::SUCCESS || retval == Retval::CONNFAIL
107
+ retval = beta_request()
108
+ end
109
+ # SLP 1.6
110
+ unless retval == Retval::CONNFAIL
111
+ retval = extended_legacy_request()
112
+ end
113
+ # SLP 1.7
114
+ unless retval == Retval::CONNFAIL
115
+ retval = json_request()
116
+ end
55
117
  end
56
- # 1.4/1.5
57
- unless retval == Retval::SUCCESS || retval == Retval::CONNFAIL
58
- retval = legacy_query(address, port, timeout)
118
+ @online = false unless retval == Retval::SUCCESS
119
+ end
120
+
121
+ # Strips message of the day formatting characters
122
+ def strip_motd()
123
+ unless @motd['text'] == nil
124
+ @stripped_motd = @motd['text']
125
+ else
126
+ @stripped_motd = @motd
59
127
  end
60
- # 1.8b/1.3
61
- unless retval == Retval::SUCCESS || retval == Retval::CONNFAIL
62
- retval = beta_query(address, port, timeout)
128
+ unless @motd['extra'] == nil
129
+ json_data = @motd['extra']
130
+ unless json_data.nil? || json_data.empty?
131
+ json_data.each do |nested_hash|
132
+ @stripped_motd += nested_hash['text']
133
+ end
134
+ end
63
135
  end
136
+ @stripped_motd = @stripped_motd.gsub(/§./, "")
137
+ end
64
138
 
65
- @online = false unless retval == Retval::SUCCESS
139
+ ##
140
+ # Establishes a connection to the Minecraft server
141
+ def connect()
142
+ begin
143
+ start_time = Time.now
144
+ @server = TCPSocket.new(@address, @port)
145
+ @latency = ((Time.now - start_time) * 1000).round
146
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
147
+ return Retval::CONNFAIL
148
+ rescue => exception
149
+ $stderr.puts exception
150
+ return Retval::UNKNOWN
151
+ end
152
+ return Retval::SUCCESS
153
+ end
154
+
155
+ # Populates object fields after connecting
156
+ def parse_data(delimiter, is_beta = false)
157
+ data = nil
158
+ begin
159
+ if @server.read(1).unpack('C').first == 0xFF # kick packet (255)
160
+ len = @server.read(2).unpack('n').first
161
+ data = @server.read(len * 2).force_encoding('UTF-16BE').encode('UTF-8')
162
+ @server.close
163
+ else
164
+ @server.close
165
+ return Retval::UNKNOWN
166
+ end
167
+ rescue => exception
168
+ #$stderr.puts exception
169
+ return Retval::UNKNOWN
170
+ end
171
+
172
+ if data == nil || data.empty?
173
+ return Retval::UNKNOWN
174
+ end
175
+
176
+ server_info = data.split(delimiter)
177
+ if is_beta
178
+ if server_info != nil && server_info.length >= NUM_FIELDS_BETA
179
+ @version = ">=1.8b/1.3" # since server does not return version, set it
180
+ @motd = server_info[0]
181
+ strip_motd
182
+ @current_players = server_info[1].to_i
183
+ @max_players = server_info[2].to_i
184
+ @online = true
185
+ else
186
+ return Retval::UNKNOWN
187
+ end
188
+ else
189
+ if server_info != nil && server_info.length >= NUM_FIELDS
190
+ # server_info[0] contains the section symbol and 1
191
+ @protocol = server_info[1].to_i # contains the protocol version (51 for 1.9 or 78 for 1.6.4 for example)
192
+ @version = server_info[2]
193
+ @motd = server_info[3]
194
+ strip_motd
195
+ @current_players = server_info[4].to_i
196
+ @max_players = server_info[5].to_i
197
+ @online = true
198
+ else
199
+ return Retval::UNKNOWN
200
+ end
201
+ end
202
+ return Retval::SUCCESS
66
203
  end
67
204
 
68
- # 1.8b/1.3
69
- # 1.8 beta through 1.3 servers communicate as follows for a ping query:
205
+ ##
206
+ # 1.8 beta through 1.3 servers communicate as follows for a ping request:
70
207
  # 1. Client sends \xFE (server list ping)
71
208
  # 2. Server responds with:
72
209
  # 2a. \xFF (kick packet)
73
210
  # 2b. data length
74
211
  # 2c. 3 fields delimited by \u00A7 (section symbol)
75
- # The 3 fields, in order, are: message of the day, current players, and max players
76
- def beta_query(address, port, timeout)
212
+ # The 3 fields, in order, are:
213
+ # * message of the day
214
+ # * current players
215
+ # * max players
216
+ def beta_request()
217
+ retval = nil
77
218
  begin
78
- data = nil
79
- Timeout::timeout(timeout) do
80
- start_time = Time.now
81
- server = TCPSocket.new(address, port)
82
- @latency = ((Time.now - start_time) * 1000).round
83
- server.write("\xFE")
84
- if server.read(1).unpack('C').first == 0xFF # kick packet (255)
85
- len = server.read(2).unpack('n').first
86
- data = server.read(len * 2).force_encoding('UTF-16BE').encode('UTF-8')
87
- server.close
88
- else
89
- server.close
90
- return Retval::UNKNOWN
91
- end
92
- end
93
-
94
- if data == nil || data.empty?
95
- return Retval::UNKNOWN
96
- else
97
- server_info = data.split("\u00A7") # section symbol
98
- if server_info != nil && server_info.length >= NUM_FIELDS_BETA
99
- @version = "1.8b/1.3" # since server does not return version, set it
100
- @motd = server_info[0]
101
- @current_players = server_info[1]
102
- @max_players = server_info[2]
103
- @online = true
104
- end
219
+ Timeout::timeout(@timeout) do
220
+ retval = connect()
221
+ return retval unless retval == Retval::SUCCESS
222
+ # Perform handshake and acquire data
223
+ @request_type = "SLP 1.8b/1.3 (beta)"
224
+ @server.write("\xFE")
225
+ retval = parse_data("\u00A7", true) # section symbol
105
226
  end
106
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
107
- return Retval::CONNFAIL
108
227
  rescue Timeout::Error
109
228
  return Retval::TIMEOUT
110
229
  rescue => exception
111
230
  $stderr.puts exception
112
231
  return Retval::UNKNOWN
113
232
  end
114
- return Retval::SUCCESS
233
+ return retval
115
234
  end
116
235
 
117
- # 1.4/1.5
118
- # 1.4 and 1.5 servers communicate as follows for a ping query:
236
+ ##
237
+ # 1.4 and 1.5 servers communicate as follows for a ping request:
119
238
  # 1. Client sends:
120
239
  # 1a. \xFE (server list ping)
121
240
  # 1b. \x01 (server list ping payload)
@@ -123,57 +242,38 @@ class MineStat
123
242
  # 2a. \xFF (kick packet)
124
243
  # 2b. data length
125
244
  # 2c. 6 fields delimited by \x00 (null)
126
- # The 6 fields, in order, are: the section symbol and 1, protocol version,
127
- # server version, message of the day, current players, and max players
245
+ # The 6 fields, in order, are:
246
+ # * the section symbol and 1
247
+ # * protocol version
248
+ # * server version
249
+ # * message of the day
250
+ # * current players
251
+ # * max players
252
+ #
128
253
  # The protocol version corresponds with the server version and can be the
129
254
  # same for different server versions.
130
- def legacy_query(address, port, timeout)
255
+ def legacy_request()
256
+ retval = nil
131
257
  begin
132
- data = nil
133
- Timeout::timeout(timeout) do
134
- start_time = Time.now
135
- server = TCPSocket.new(address, port)
136
- @latency = ((Time.now - start_time) * 1000).round
137
- server.write("\xFE\x01")
138
- if server.read(1).unpack('C').first == 0xFF # kick packet (255)
139
- len = server.read(2).unpack('n').first
140
- data = server.read(len * 2).force_encoding('UTF-16BE').encode('UTF-8')
141
- server.close
142
- else
143
- server.close
144
- return Retval::UNKNOWN
145
- end
146
- end
147
-
148
- if data == nil || data.empty?
149
- return Retval::UNKNOWN
150
- else
151
- server_info = data.split("\x00") # null
152
- if server_info != nil && server_info.length >= NUM_FIELDS
153
- # server_info[0] contains the section symbol and 1
154
- # server_info[1] contains the protocol version (51 for example)
155
- @version = server_info[2]
156
- @motd = server_info[3]
157
- @current_players = server_info[4]
158
- @max_players = server_info[5]
159
- @online = true
160
- else
161
- return Retval::UNKNOWN
162
- end
258
+ Timeout::timeout(@timeout) do
259
+ retval = connect()
260
+ return retval unless retval == Retval::SUCCESS
261
+ # Perform handshake and acquire data
262
+ @request_type = "SLP 1.4/1.5 (legacy)"
263
+ @server.write("\xFE\x01")
264
+ retval = parse_data("\x00") # null
163
265
  end
164
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
165
- return Retval::CONNFAIL
166
266
  rescue Timeout::Error
167
267
  return Retval::TIMEOUT
168
268
  rescue => exception
169
269
  $stderr.puts exception
170
270
  return Retval::UNKNOWN
171
271
  end
172
- return Retval::SUCCESS
272
+ return retval
173
273
  end
174
274
 
175
- # 1.6
176
- # 1.6 servers communicate as follows for a ping query:
275
+ ##
276
+ # 1.6 servers communicate as follows for a ping request:
177
277
  # 1. Client sends:
178
278
  # 1a. \xFE (server list ping)
179
279
  # 1b. \x01 (server list ping payload)
@@ -189,64 +289,45 @@ class MineStat
189
289
  # 2a. \xFF (kick packet)
190
290
  # 2b. data length
191
291
  # 2c. 6 fields delimited by \x00 (null)
192
- # The 6 fields, in order, are: the section symbol and 1, protocol version,
193
- # server version, message of the day, current players, and max players
292
+ # The 6 fields, in order, are:
293
+ # * the section symbol and 1
294
+ # * protocol version
295
+ # * server version
296
+ # * message of the day
297
+ # * current players
298
+ # * max players
299
+ #
194
300
  # The protocol version corresponds with the server version and can be the
195
301
  # same for different server versions.
196
- def new_query(address, port, timeout)
302
+ def extended_legacy_request()
303
+ retval = nil
197
304
  begin
198
- data = nil
199
- Timeout::timeout(DEFAULT_TIMEOUT) do
200
- start_time = Time.now
201
- server = TCPSocket.new(address, port)
202
- @latency = ((Time.now - start_time) * 1000).round
203
- server.write("\xFE\x01\xFA")
204
- server.write("\x00\x0B") # 11 (length of "MC|PingHost")
205
- server.write('MC|PingHost'.encode('UTF-16BE').force_encoding('ASCII-8BIT'))
206
- server.write([7 + 2 * address.length].pack('n'))
207
- server.write("\x4E") # 78 (protocol version of 1.6.4)
208
- server.write([address.length].pack('n'))
209
- server.write(address.encode('UTF-16BE').force_encoding('ASCII-8BIT'))
210
- server.write([port].pack('N'))
211
- if server.read(1).unpack('C').first == 0xFF # kick packet (255)
212
- len = server.read(2).unpack('n').first
213
- data = server.read(len * 2).force_encoding('UTF-16BE').encode('UTF-8')
214
- server.close
215
- else
216
- server.close
217
- return Retval::UNKNOWN
218
- end
219
- end
220
-
221
- if data == nil || data.empty?
222
- return Retval::UNKNOWN
223
- else
224
- server_info = data.split("\x00") # null
225
- if server_info != nil && server_info.length >= NUM_FIELDS
226
- # server_info[0] contains the section symbol and 1
227
- # server_info[1] contains the protocol version (78 for example)
228
- @version = server_info[2]
229
- @motd = server_info[3]
230
- @current_players = server_info[4]
231
- @max_players = server_info[5]
232
- @online = true
233
- else
234
- return Retval::UNKNOWN
235
- end
305
+ Timeout::timeout(@timeout) do
306
+ retval = connect()
307
+ return retval unless retval == Retval::SUCCESS
308
+ # Perform handshake and acquire data
309
+ @request_type = "SLP 1.6 (extended legacy)"
310
+ @server.write("\xFE\x01\xFA")
311
+ @server.write("\x00\x0B") # 11 (length of "MC|PingHost")
312
+ @server.write('MC|PingHost'.encode('UTF-16BE').force_encoding('ASCII-8BIT'))
313
+ @server.write([7 + 2 * @address.length].pack('n'))
314
+ @server.write("\x4E") # 78 (protocol version of 1.6.4)
315
+ @server.write([@address.length].pack('n'))
316
+ @server.write(@address.encode('UTF-16BE').force_encoding('ASCII-8BIT'))
317
+ @server.write([@port].pack('N'))
318
+ retval = parse_data("\x00") # null
236
319
  end
237
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
238
- return Retval::CONNFAIL
239
320
  rescue Timeout::Error
240
321
  return Retval::TIMEOUT
241
322
  rescue => exception
242
323
  $stderr.puts exception
243
324
  return Retval::UNKNOWN
244
325
  end
245
- return Retval::SUCCESS
326
+ return retval
246
327
  end
247
328
 
248
- # 1.7
249
- # 1.7 to current servers communicate as follows for a ping query:
329
+ ##
330
+ # 1.7 to current servers communicate as follows for a ping request:
250
331
  # 1. Client sends:
251
332
  # 1a. \x00 (handshake packet containing the fields specified below)
252
333
  # 1b. \x00 (request)
@@ -261,9 +342,128 @@ class MineStat
261
342
  # {'players': {'max': 20, 'online': 0},
262
343
  # 'version': {'protocol': 404, 'name': '1.13.2'},
263
344
  # 'description': {'text': 'A Minecraft Server'}}
264
- def json_query(address, port, timeout)
265
- return Retval::UNKNOWN # ToDo: Implement me!
345
+ def json_request()
346
+ retval = nil
347
+ begin
348
+ Timeout::timeout(@timeout) do
349
+ retval = connect()
350
+ return retval unless retval == Retval::SUCCESS
351
+ # Perform handshake
352
+ @request_type = "SLP 1.7 (JSON)"
353
+ payload = "\x00\x00"
354
+ payload += [@address.length].pack('c') << @address
355
+ payload += [@port].pack('n')
356
+ payload += "\x01"
357
+ payload = [payload.length].pack('c') << payload
358
+ @server.write(payload)
359
+ @server.write("\x01\x00")
360
+ @server.flush
361
+
362
+ # Acquire data
363
+ _total_len = unpack_varint
364
+ return Retval::UNKNOWN if unpack_varint != 0
365
+ json_len = unpack_varint
366
+ json_data = recv_json(json_len)
367
+ @server.close
368
+
369
+ # Parse data
370
+ json_data = JSON.parse(json_data)
371
+ @json_data = json_data
372
+ @protocol = json_data['version']['protocol'].to_i
373
+ @version = json_data['version']['name']
374
+ @motd = json_data['description']
375
+ strip_motd
376
+ @current_players = json_data['players']['online'].to_i
377
+ @max_players = json_data['players']['max'].to_i
378
+ if !@version.empty? && !@motd.empty? && !@current_players.nil? && !@max_players.nil?
379
+ @online = true
380
+ else
381
+ retval = Retval::UNKNOWN
382
+ end
383
+ end
384
+ rescue Timeout::Error
385
+ return Retval::TIMEOUT
386
+ rescue JSON::ParserError
387
+ return Retval::UNKNOWN
388
+ rescue => exception
389
+ $stderr.puts exception
390
+ return Retval::UNKNOWN
391
+ end
392
+ return retval
393
+ end
394
+
395
+ # Reads JSON data from the socket
396
+ def recv_json(json_len)
397
+ json_data = ""
398
+ begin
399
+ loop do
400
+ remaining = json_len - json_data.length
401
+ data = @server.recv(remaining)
402
+ @server.flush
403
+ json_data += data
404
+ break if json_data.length >= json_len
405
+ end
406
+ rescue => exception
407
+ $stderr.puts exception
408
+ end
409
+ return json_data
266
410
  end
267
411
 
268
- attr_reader :address, :port, :online, :version, :motd, :current_players, :max_players, :latency
412
+ # Returns value of varint type
413
+ def unpack_varint()
414
+ vint = 0
415
+ i = 0
416
+ while i <= MAX_VARINT_SIZE
417
+ data = @server.read(1)
418
+ return 0 if data.nil? || data.empty?
419
+ data = data.ord
420
+ vint |= (data & 0x7F) << 7 * i
421
+ break if (data & 0x80) != 128
422
+ i += 1
423
+ end
424
+ return vint
425
+ end
426
+
427
+ # Returns the Minecraft server IP
428
+ attr_reader :address
429
+
430
+ # Returns the Minecraft server TCP port
431
+ attr_reader :port
432
+
433
+ # Returns a boolean describing whether the server is online or offline
434
+ attr_reader :online
435
+
436
+ # Returns the Minecraft version that the server is running
437
+ attr_reader :version
438
+
439
+ # Returns the full version of the MOTD
440
+ #
441
+ # If you just want the MOTD text, use stripped_motd
442
+ attr_reader :motd
443
+
444
+ # Returns just the plain text contained within the MOTD
445
+ attr_reader :stripped_motd
446
+
447
+ # Returns the current player count
448
+ attr_reader :current_players
449
+
450
+ # Returns the maximum player count
451
+ attr_reader :max_players
452
+
453
+ # Returns the SLP (Server List Ping) protocol level
454
+ #
455
+ # This is arbitrary and varies by Minecraft version.
456
+ # However, multiple Minecraft versions can share the same
457
+ # protocol level
458
+ attr_reader :protocol
459
+
460
+ # Returns the complete JSON response data for queries to Minecraft
461
+ # servers with a version greater than or equal to 1.7
462
+ attr_reader :json_data
463
+
464
+ # Returns the ping time to the server in ms
465
+ attr_reader :latency
466
+
467
+ # Returns the SLP (Server List Ping) protocol version
468
+ attr_reader :request_type
269
469
  end
metadata CHANGED
@@ -1,18 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minestat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lloyd Dilley
8
8
  - Stepan Melnikov
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-26 00:00:00.000000000 Z
12
+ date: 2021-09-27 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: MineStat polls Minecraft server data such as version, motd, current players,
15
- and max players using a variety of programming languages.
15
+ and max players.
16
16
  email:
17
17
  - minecraft@frag.land
18
18
  executables: []
@@ -24,7 +24,7 @@ homepage: https://github.com/FragLand/minestat
24
24
  licenses:
25
25
  - GPL-3.0
26
26
  metadata: {}
27
- post_install_message:
27
+ post_install_message:
28
28
  rdoc_options: []
29
29
  require_paths:
30
30
  - lib
@@ -39,9 +39,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
- rubyforge_project:
43
- rubygems_version: 2.7.6.2
44
- signing_key:
42
+ rubygems_version: 3.2.22
43
+ signing_key:
45
44
  specification_version: 4
46
45
  summary: Minecraft server status checker
47
46
  test_files: []