mirror of
https://github.com/Dinnerbone/mcstatus.git
synced 2026-04-06 03:51:23 +08:00
Added initial work for server pinging. Normal pings work, Query NYI.
This commit is contained in:
54
mcstatus/pinger.py
Normal file
54
mcstatus/pinger.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import datetime
|
||||||
|
import random
|
||||||
|
|
||||||
|
from mcstatus.protocol.connection import Connection
|
||||||
|
|
||||||
|
|
||||||
|
class ServerPinger:
|
||||||
|
def __init__(self, connection, host="", port=0, version=47, ping_token=None):
|
||||||
|
if ping_token is None:
|
||||||
|
ping_token = random.randint(0, 1 << 63 - 1)
|
||||||
|
self.version = version
|
||||||
|
self.connection = connection
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.ping_token = ping_token
|
||||||
|
|
||||||
|
def handshake(self):
|
||||||
|
packet = Connection()
|
||||||
|
packet.write_varint(0)
|
||||||
|
packet.write_varint(self.version)
|
||||||
|
packet.write_utf(self.host)
|
||||||
|
packet.write_short(self.port)
|
||||||
|
packet.write_varint(1) # Intention to query status
|
||||||
|
|
||||||
|
self.connection.write_buffer(packet)
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
request = Connection()
|
||||||
|
request.write_varint(0) # Request status
|
||||||
|
self.connection.write_buffer(request)
|
||||||
|
|
||||||
|
response = self.connection.read_buffer()
|
||||||
|
if response.read_varint() != 0:
|
||||||
|
raise IOError("Received invalid query response packet.")
|
||||||
|
return response.read_utf()
|
||||||
|
|
||||||
|
def test_ping(self):
|
||||||
|
request = Connection()
|
||||||
|
request.write_varint(1) # Test ping
|
||||||
|
request.write_long(self.ping_token)
|
||||||
|
sent = datetime.datetime.now()
|
||||||
|
self.connection.write_buffer(request)
|
||||||
|
|
||||||
|
response = self.connection.read_buffer()
|
||||||
|
received = datetime.datetime.now()
|
||||||
|
if response.read_varint() != 1:
|
||||||
|
raise IOError("Received invalid ping response packet.")
|
||||||
|
received_token = response.read_long()
|
||||||
|
if received_token != self.ping_token:
|
||||||
|
raise IOError("Received mangled ping response packet (expected token %d, received %d)" % (self.ping_token, received_token))
|
||||||
|
|
||||||
|
delta = (received - sent)
|
||||||
|
# We have no trivial way of getting a time delta :(
|
||||||
|
return (delta.days * 24 * 60 * 60 + delta.seconds) * 1000 + delta.microseconds / 1000.0
|
||||||
0
mcstatus/protocol/__init__.py
Normal file
0
mcstatus/protocol/__init__.py
Normal file
115
mcstatus/protocol/connection.py
Normal file
115
mcstatus/protocol/connection.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
def __init__(self):
|
||||||
|
self.sent = bytearray()
|
||||||
|
self.received = bytearray()
|
||||||
|
|
||||||
|
def read(self, length):
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
result = self.received[:length]
|
||||||
|
self.received = self.received[length:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
self.sent += data
|
||||||
|
|
||||||
|
def receive(self, data):
|
||||||
|
self.received += data
|
||||||
|
|
||||||
|
def remaining(self):
|
||||||
|
return len(self.received)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
result = self.sent
|
||||||
|
self.sent = ""
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_varint(self):
|
||||||
|
result = 0
|
||||||
|
for i in range(5):
|
||||||
|
part = ord(self.read(1))
|
||||||
|
result |= (part & 0x7F) << 7 * i
|
||||||
|
if not part & 0x80:
|
||||||
|
return result
|
||||||
|
raise IOError("Server sent a varint that was too big!")
|
||||||
|
|
||||||
|
def write_varint(self, value):
|
||||||
|
remaining = value
|
||||||
|
for i in range(5):
|
||||||
|
if remaining & ~0x7F == 0:
|
||||||
|
self.write(chr(remaining))
|
||||||
|
return
|
||||||
|
self.write(chr(remaining & 0x7F | 0x80))
|
||||||
|
remaining >>= 7
|
||||||
|
raise ValueError("The value %d is too big to send in a varint" % value)
|
||||||
|
|
||||||
|
def read_utf(self):
|
||||||
|
length = self.read_varint()
|
||||||
|
return str(self.read(length)).encode('utf8')
|
||||||
|
|
||||||
|
def write_utf(self, value):
|
||||||
|
self.write_varint(len(value))
|
||||||
|
self.write(bytearray(value.decode('utf8'), 'utf8'))
|
||||||
|
|
||||||
|
def read_short(self):
|
||||||
|
return struct.unpack(">h", str(self.read(2)))[0]
|
||||||
|
|
||||||
|
def write_short(self, value):
|
||||||
|
self.write(struct.pack(">h", value))
|
||||||
|
|
||||||
|
def read_ushort(self):
|
||||||
|
return struct.unpack(">H", str(self.read(2)))[0]
|
||||||
|
|
||||||
|
def write_ushort(self, value):
|
||||||
|
self.write(struct.pack(">H", value))
|
||||||
|
|
||||||
|
def read_long(self):
|
||||||
|
return struct.unpack(">q", str(self.read(8)))[0]
|
||||||
|
|
||||||
|
def write_long(self, value):
|
||||||
|
self.write(struct.pack(">q", value))
|
||||||
|
|
||||||
|
def read_ulong(self):
|
||||||
|
return struct.unpack(">Q", str(self.read(8)))[0]
|
||||||
|
|
||||||
|
def write_ulong(self, value):
|
||||||
|
self.write(struct.pack(">Q", value))
|
||||||
|
|
||||||
|
def read_buffer(self):
|
||||||
|
length = self.read_varint()
|
||||||
|
result = Connection()
|
||||||
|
result.receive(self.read(length))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def write_buffer(self, buffer):
|
||||||
|
data = buffer.flush()
|
||||||
|
self.write_varint(len(data))
|
||||||
|
self.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
class TCPSocketConnection(Connection):
|
||||||
|
def __init__(self, addr):
|
||||||
|
Connection.__init__(self)
|
||||||
|
self.socket = socket.create_connection(addr, timeout=10)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
raise TypeError("SocketConnection does not support flush()")
|
||||||
|
|
||||||
|
def receive(self, data):
|
||||||
|
raise TypeError("SocketConnection does not support receive()")
|
||||||
|
|
||||||
|
def remaining(self):
|
||||||
|
raise TypeError("SocketConnection does not support remaining()")
|
||||||
|
|
||||||
|
def read(self, length):
|
||||||
|
result = ""
|
||||||
|
while len(result) < length:
|
||||||
|
result += self.socket.recv(length - len(result))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
self.socket.send(data)
|
||||||
22
mcstatus/server.py
Normal file
22
mcstatus/server.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import json
|
||||||
|
import socket
|
||||||
|
from mcstatus.pinger import ServerPinger
|
||||||
|
from mcstatus.protocol.connection import TCPSocketConnection
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftServer:
|
||||||
|
def __init__(self, host, port=25565):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
def ping_server(self, **kwargs):
|
||||||
|
connection = TCPSocketConnection((self.host, self.port))
|
||||||
|
pinger = ServerPinger(connection, host=self.host, port=self.port, **kwargs)
|
||||||
|
pinger.handshake()
|
||||||
|
try :
|
||||||
|
return {
|
||||||
|
"status": json.loads(pinger.read_status()),
|
||||||
|
"latency": pinger.test_ping(),
|
||||||
|
}
|
||||||
|
except ValueError as ex:
|
||||||
|
raise IOError("The server responded with invalid json")
|
||||||
0
mcstatus/tests/__init__.py
Normal file
0
mcstatus/tests/__init__.py
Normal file
1
mcstatus/tests/protocol/__init__.py
Normal file
1
mcstatus/tests/protocol/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__author__ = 'Dinnerbone'
|
||||||
185
mcstatus/tests/protocol/test_connection.py
Normal file
185
mcstatus/tests/protocol/test_connection.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import socket
|
||||||
|
from unittest import TestCase
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
from mcstatus.protocol.connection import Connection, TCPSocketConnection
|
||||||
|
|
||||||
|
|
||||||
|
class TestConnection(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.connection = Connection()
|
||||||
|
|
||||||
|
def test_flush(self):
|
||||||
|
self.connection.sent = "\x7F\xAA\xBB"
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x7F\xAA\xBB")
|
||||||
|
self.assertTrue(self.connection.sent == "")
|
||||||
|
|
||||||
|
def test_receive(self):
|
||||||
|
self.connection.receive("\x7F")
|
||||||
|
self.connection.receive("\xAA\xBB")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.received, "\x7F\xAA\xBB")
|
||||||
|
|
||||||
|
def test_remaining(self):
|
||||||
|
self.connection.receive("\x7F")
|
||||||
|
self.connection.receive("\xAA\xBB")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.remaining(), 3)
|
||||||
|
|
||||||
|
def test_send(self):
|
||||||
|
self.connection.write("\x7F")
|
||||||
|
self.connection.write("\xAA\xBB")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x7F\xAA\xBB")
|
||||||
|
|
||||||
|
def test_read(self):
|
||||||
|
self.connection.receive("\x7F\xAA\xBB")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read(2), "\x7F\xAA")
|
||||||
|
self.assertEqual(self.connection.read(1), "\xBB")
|
||||||
|
|
||||||
|
def test_readSimpleVarInt(self):
|
||||||
|
self.connection.receive("\x0F")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_varint(), 15)
|
||||||
|
|
||||||
|
def test_writeSimpleVarInt(self):
|
||||||
|
self.connection.write_varint(15)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x0F")
|
||||||
|
|
||||||
|
def test_readBigVarInt(self):
|
||||||
|
self.connection.receive("\xFF\xFF\xFF\xFF\x7F")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_varint(), 34359738367)
|
||||||
|
|
||||||
|
def test_writeBigVarInt(self):
|
||||||
|
self.connection.write_varint(2147483647)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\xFF\xFF\xFF\xFF\x07")
|
||||||
|
|
||||||
|
def test_readInvalidVarInt(self):
|
||||||
|
self.connection.receive("\xFF\xFF\xFF\xFF\x80")
|
||||||
|
|
||||||
|
self.assertRaises(IOError, self.connection.read_varint)
|
||||||
|
|
||||||
|
def test_writeInvalidVarInt(self):
|
||||||
|
self.assertRaises(ValueError, self.connection.write_varint, 34359738368)
|
||||||
|
|
||||||
|
def test_readString(self):
|
||||||
|
self.connection.receive("\x0D\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_utf(), "Hello, world!")
|
||||||
|
|
||||||
|
def test_writeString(self):
|
||||||
|
self.connection.write_utf("Hello, world!")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x0D\x48\x65\x6C\x6C\x6F\x2C\x20\x77\x6F\x72\x6C\x64\x21")
|
||||||
|
|
||||||
|
def test_readEmptyString(self):
|
||||||
|
self.connection.write_utf("")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x00")
|
||||||
|
|
||||||
|
def test_readShortNegative(self):
|
||||||
|
self.connection.receive("\x80\x00")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_short(), -32768)
|
||||||
|
|
||||||
|
def test_writeShortNegative(self):
|
||||||
|
self.connection.write_short(-32768)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x80\x00")
|
||||||
|
|
||||||
|
def test_readShortPositive(self):
|
||||||
|
self.connection.receive("\x7F\xFF")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_short(), 32767)
|
||||||
|
|
||||||
|
def test_writeShortPositive(self):
|
||||||
|
self.connection.write_short(32767)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x7F\xFF")
|
||||||
|
|
||||||
|
def test_readUShortPositive(self):
|
||||||
|
self.connection.receive("\x80\x00")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_ushort(), 32768)
|
||||||
|
|
||||||
|
def test_writeUShortPositive(self):
|
||||||
|
self.connection.write_ushort(32768)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x80\x00")
|
||||||
|
|
||||||
|
def test_readLongNegative(self):
|
||||||
|
self.connection.receive("\x80\x00\x00\x00\x00\x00\x00\x00")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_long(), -9223372036854775808)
|
||||||
|
|
||||||
|
def test_writeLongNegative(self):
|
||||||
|
self.connection.write_long(-9223372036854775808)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x80\x00\x00\x00\x00\x00\x00\x00")
|
||||||
|
|
||||||
|
def test_readLongPositive(self):
|
||||||
|
self.connection.receive("\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_long(), 9223372036854775807)
|
||||||
|
|
||||||
|
def test_writeLongPositive(self):
|
||||||
|
self.connection.write_long(9223372036854775807)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF")
|
||||||
|
|
||||||
|
def test_readULongPositive(self):
|
||||||
|
self.connection.receive("\x80\x00\x00\x00\x00\x00\x00\x00")
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read_ulong(), 9223372036854775808)
|
||||||
|
|
||||||
|
def test_writeULongPositive(self):
|
||||||
|
self.connection.write_ulong(9223372036854775808)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x80\x00\x00\x00\x00\x00\x00\x00")
|
||||||
|
|
||||||
|
def test_readBuffer(self):
|
||||||
|
self.connection.receive("\x02\x7F\xAA")
|
||||||
|
buffer = self.connection.read_buffer()
|
||||||
|
|
||||||
|
self.assertEqual(buffer.received, "\x7F\xAA")
|
||||||
|
self.assertEqual(self.connection.flush(), "")
|
||||||
|
|
||||||
|
def test_writeBuffer(self):
|
||||||
|
buffer = Connection()
|
||||||
|
buffer.write("\x7F\xAA")
|
||||||
|
self.connection.write_buffer(buffer)
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.flush(), "\x02\x7F\xAA")
|
||||||
|
|
||||||
|
class TCPSocketConnectionTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
socket = Mock()
|
||||||
|
socket.recv = Mock()
|
||||||
|
socket.send = Mock()
|
||||||
|
with patch("socket.create_connection") as create_connection:
|
||||||
|
create_connection.return_value = socket
|
||||||
|
self.connection = TCPSocketConnection(("localhost", 1234))
|
||||||
|
|
||||||
|
def test_flush(self):
|
||||||
|
self.assertRaises(TypeError, self.connection.flush)
|
||||||
|
|
||||||
|
def test_receive(self):
|
||||||
|
self.assertRaises(TypeError, self.connection.receive, "")
|
||||||
|
|
||||||
|
def test_remaining(self):
|
||||||
|
self.assertRaises(TypeError, self.connection.remaining)
|
||||||
|
|
||||||
|
def test_read(self):
|
||||||
|
self.connection.socket.recv.return_value = "\x7F\xAA"
|
||||||
|
|
||||||
|
self.assertEqual(self.connection.read(2), "\x7F\xAA")
|
||||||
|
|
||||||
|
def test_write(self):
|
||||||
|
self.connection.write("\x7F\xAA")
|
||||||
|
|
||||||
|
self.connection.socket.send.assert_called_once_with("\x7F\xAA")
|
||||||
44
mcstatus/tests/protocol/test_pinger.py
Normal file
44
mcstatus/tests/protocol/test_pinger.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from mcstatus.protocol.connection import Connection
|
||||||
|
from mcstatus.pinger import ServerPinger
|
||||||
|
|
||||||
|
|
||||||
|
class TestServerPinger(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.pinger = ServerPinger(Connection(), host="localhost", port=25565, version=44)
|
||||||
|
|
||||||
|
def test_handshake(self):
|
||||||
|
self.pinger.handshake()
|
||||||
|
|
||||||
|
self.assertEqual(self.pinger.connection.flush(), "\x0F\x00\x2C\x09\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x63\xDD\x01")
|
||||||
|
|
||||||
|
def test_read_status(self):
|
||||||
|
self.pinger.connection.receive("\x72\x00\x70\x7B\x22\x64\x65\x73\x63\x72\x69\x70\x74\x69\x6F\x6E\x22\x3A\x22\x41\x20\x4D\x69\x6E\x65\x63\x72\x61\x66\x74\x20\x53\x65\x72\x76\x65\x72\x22\x2C\x22\x70\x6C\x61\x79\x65\x72\x73\x22\x3A\x7B\x22\x6D\x61\x78\x22\x3A\x32\x30\x2C\x22\x6F\x6E\x6C\x69\x6E\x65\x22\x3A\x30\x7D\x2C\x22\x76\x65\x72\x73\x69\x6F\x6E\x22\x3A\x7B\x22\x6E\x61\x6D\x65\x22\x3A\x22\x31\x2E\x38\x2D\x70\x72\x65\x31\x22\x2C\x22\x70\x72\x6F\x74\x6F\x63\x6F\x6C\x22\x3A\x34\x34\x7D\x7D")
|
||||||
|
|
||||||
|
self.assertEqual(self.pinger.read_status(), '{"description":"A Minecraft Server","players":{"max":20,"online":0},"version":{"name":"1.8-pre1","protocol":44}}')
|
||||||
|
self.assertEqual(self.pinger.connection.flush(), "\x01\x00")
|
||||||
|
|
||||||
|
def test_read_status_invalid(self):
|
||||||
|
self.pinger.connection.receive("\x01\x05")
|
||||||
|
|
||||||
|
self.assertRaises(IOError, self.pinger.read_status)
|
||||||
|
|
||||||
|
def test_test_ping(self):
|
||||||
|
self.pinger.connection.receive("\x09\x01\x00\x00\x00\x00\x00\xDD\x7D\x1C")
|
||||||
|
self.pinger.ping_token = 14515484
|
||||||
|
|
||||||
|
self.assertTrue(self.pinger.test_ping() >= 0)
|
||||||
|
self.assertEqual(self.pinger.connection.flush(), "\x09\x01\x00\x00\x00\x00\x00\xDD\x7D\x1C")
|
||||||
|
|
||||||
|
def test_test_ping_invalid(self):
|
||||||
|
self.pinger.connection.receive("\x01\x1F")
|
||||||
|
self.pinger.ping_token = 14515484
|
||||||
|
|
||||||
|
self.assertRaises(IOError, self.pinger.test_ping)
|
||||||
|
|
||||||
|
def test_test_ping_wrong_token(self):
|
||||||
|
self.pinger.connection.receive("\x09\x01\x00\x00\x00\x00\x00\xDD\x7D\x1C")
|
||||||
|
self.pinger.ping_token = 12345
|
||||||
|
|
||||||
|
self.assertRaises(IOError, self.pinger.test_ping)
|
||||||
29
mcstatus/tests/test_server.py
Normal file
29
mcstatus/tests/test_server.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
from mock import Mock, patch
|
||||||
|
from mcstatus.protocol.connection import Connection
|
||||||
|
from mcstatus.server import MinecraftServer
|
||||||
|
|
||||||
|
|
||||||
|
class TestMinecraftServer(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.socket = Connection()
|
||||||
|
self.server = MinecraftServer("localhost", port=25565)
|
||||||
|
|
||||||
|
def test_ping_server(self):
|
||||||
|
self.socket.receive("\x6D\x00\x6B\x7B\x22\x64\x65\x73\x63\x72\x69\x70\x74\x69\x6F\x6E\x22\x3A\x22\x41\x20\x4D\x69\x6E\x65\x63\x72\x61\x66\x74\x20\x53\x65\x72\x76\x65\x72\x22\x2C\x22\x70\x6C\x61\x79\x65\x72\x73\x22\x3A\x7B\x22\x6D\x61\x78\x22\x3A\x32\x30\x2C\x22\x6F\x6E\x6C\x69\x6E\x65\x22\x3A\x30\x7D\x2C\x22\x76\x65\x72\x73\x69\x6F\x6E\x22\x3A\x7B\x22\x6E\x61\x6D\x65\x22\x3A\x22\x31\x2E\x38\x22\x2C\x22\x70\x72\x6F\x74\x6F\x63\x6F\x6C\x22\x3A\x34\x37\x7D\x7D\x09\x01\x00\x00\x00\x00\x01\xC5\x42\x46")
|
||||||
|
|
||||||
|
with patch("mcstatus.server.TCPSocketConnection") as connection:
|
||||||
|
connection.return_value = self.socket
|
||||||
|
info = self.server.ping_server(ping_token=29704774, version=47)
|
||||||
|
|
||||||
|
self.assertEqual(self.socket.flush(), "\x0F\x00\x2F\x09\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x63\xDD\x01\x01\x00\x09\x01\x00\x00\x00\x00\x01\xC5\x42\x46")
|
||||||
|
self.assertEqual(self.socket.remaining(), 0, msg="Data is pending to be read, but should be empty")
|
||||||
|
self.assertEqual(info["status"], {"description":"A Minecraft Server","players":{"max":20,"online":0},"version":{"name":"1.8","protocol":47}})
|
||||||
|
self.assertTrue(info["latency"] >= 0)
|
||||||
|
|
||||||
|
def test_ping_server_invalid_json(self):
|
||||||
|
self.socket.receive("\x6D\x00\x6B\x7C\x22\x64\x65\x73\x63\x72\x69\x70\x74\x69\x6F\x6E\x22\x3A\x22\x41\x20\x4D\x69\x6E\x65\x63\x72\x61\x66\x74\x20\x53\x65\x72\x76\x65\x72\x22\x2C\x22\x70\x6C\x61\x79\x65\x72\x73\x22\x3A\x7B\x22\x6D\x61\x78\x22\x3A\x32\x30\x2C\x22\x6F\x6E\x6C\x69\x6E\x65\x22\x3A\x30\x7D\x2C\x22\x76\x65\x72\x73\x69\x6F\x6E\x22\x3A\x7B\x22\x6E\x61\x6D\x65\x22\x3A\x22\x31\x2E\x38\x22\x2C\x22\x70\x72\x6F\x74\x6F\x63\x6F\x6C\x22\x3A\x34\x37\x7D\x7D\x09\x01\x00\x00\x00\x00\x01\xC5\x42\x46")
|
||||||
|
|
||||||
|
with patch("mcstatus.server.TCPSocketConnection") as connection:
|
||||||
|
connection.return_value = self.socket
|
||||||
|
self.assertRaises(IOError, self.server.ping_server, ping_token=29704774, version=47)
|
||||||
5
setup.py
5
setup.py
@@ -2,10 +2,11 @@ from distutils.core import setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='mcstatus',
|
name='mcstatus',
|
||||||
version='1.0',
|
version='2.0dev',
|
||||||
author='Nathan Adams',
|
author='Nathan Adams',
|
||||||
author_email='dinnerbone@dinnerbone.com',
|
author_email='dinnerbone@dinnerbone.com',
|
||||||
url='https://pypi.python.org/pypi/mcstatus',
|
url='https://pypi.python.org/pypi/mcstatus',
|
||||||
packages=['minecraft_query',],
|
packages=['minecraft_query',],
|
||||||
description='A library to query Minecraft Servers for their status and capabilities.'
|
description='A library to query Minecraft Servers for their status and capabilities.',
|
||||||
|
tests_require=['mock'],
|
||||||
)
|
)
|
||||||
Reference in New Issue
Block a user