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.
- checksums.yaml +4 -4
- data/lib/minestat.rb +349 -149
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2824d765e8d40dd3a1087567b27a9a04c9df93427b344c8e47346a0a778f5be
|
4
|
+
data.tar.gz: 5974801cb768bc9a67cd789a0296b569c1f9b14442d7124135c3ddbd8d41886b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
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
|
-
|
69
|
-
# 1.8 beta through 1.3 servers communicate as follows for a ping
|
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:
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@
|
83
|
-
server.write("\xFE")
|
84
|
-
|
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
|
233
|
+
return retval
|
115
234
|
end
|
116
235
|
|
117
|
-
|
118
|
-
# 1.4 and 1.5 servers communicate as follows for a ping
|
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:
|
127
|
-
#
|
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
|
255
|
+
def legacy_request()
|
256
|
+
retval = nil
|
131
257
|
begin
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@
|
137
|
-
server.write("\xFE\x01")
|
138
|
-
|
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
|
272
|
+
return retval
|
173
273
|
end
|
174
274
|
|
175
|
-
|
176
|
-
# 1.6 servers communicate as follows for a ping
|
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:
|
193
|
-
#
|
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
|
302
|
+
def extended_legacy_request()
|
303
|
+
retval = nil
|
197
304
|
begin
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
@
|
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
|
-
|
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
|
326
|
+
return retval
|
246
327
|
end
|
247
328
|
|
248
|
-
|
249
|
-
# 1.7 to current servers communicate as follows for a ping
|
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
|
265
|
-
|
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
|
-
|
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:
|
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:
|
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
|
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
|
-
|
43
|
-
|
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: []
|