diff --git a/minecraft/authentication.py b/minecraft/authentication.py index 8a65665..99ad640 100644 --- a/minecraft/authentication.py +++ b/minecraft/authentication.py @@ -4,10 +4,9 @@ Handles authentication with the Mojang authentication server. import requests import json from .exceptions import YggdrasilError -import collections -AUTHSERVER = "https://authserver.mojang.com" -SESSIONSERVER = "https://sessionserver.mojang.com/session/minecraft" +AUTH_SERVER = "https://authserver.mojang.com" +SESSION_SERVER = "https://sessionserver.mojang.com/session/minecraft" # Need this content type, or authserver will complain CONTENT_TYPE = "application/json" HEADERS = {"content-type": CONTENT_TYPE} @@ -113,7 +112,7 @@ class AuthenticationToken(object): "password": password } - req = _make_request(AUTHSERVER, "authenticate", payload) + req = _make_request(AUTH_SERVER, "authenticate", payload) _raise_from_request(req) @@ -148,7 +147,7 @@ class AuthenticationToken(object): if self.client_token is None: raise ValueError("'client_token' is not set!") - req = _make_request(AUTHSERVER, + req = _make_request(AUTH_SERVER, "refresh", {"accessToken": self.access_token, "clientToken": self.client_token}) @@ -180,7 +179,7 @@ class AuthenticationToken(object): if self.access_token is None: raise ValueError("'access_token' not set!") - req = _make_request(AUTHSERVER, "validate", + req = _make_request(AUTH_SERVER, "validate", {"accessToken": self.access_token}) if _raise_from_request(req) is None: @@ -203,8 +202,8 @@ class AuthenticationToken(object): Raises: minecraft.exceptions.YggdrasilError """ - req = _make_request(AUTHSERVER, "signout", {"username": username, - "password": password}) + req = _make_request(AUTH_SERVER, "signout", + {"username": username, "password": password}) if _raise_from_request(req) is None: return True @@ -220,11 +219,11 @@ class AuthenticationToken(object): Raises: :class:`minecraft.exceptions.YggdrasilError` """ - req = _make_request(AUTHSERVER, "invalidate", + req = _make_request(AUTH_SERVER, "invalidate", {"accessToken": self.access_token, "clientToken": self.client_token}) - if status_code == requests.codes.ok and not req.text: + if not req.raise_for_status() and not req.text: return True else: raise YggdrasilError("Failed to invalidate tokens.") @@ -248,7 +247,7 @@ class AuthenticationToken(object): err = "AuthenticationToken hasn't been authenticated yet!" raise YggdrasilError(err) - req = _make_request(SESSIONSERVER, "join", + req = _make_request(SESSION_SERVER, "join", {"accessToken": self.access_token, "selectedProfile": self.profile.to_dict(), "serverId": server_id}) diff --git a/minecraft/networking/connection.py b/minecraft/networking/connection.py index 1e39398..68731bd 100644 --- a/minecraft/networking/connection.py +++ b/minecraft/networking/connection.py @@ -9,7 +9,7 @@ import select import packets from types import VarInt -from minecraft import PROTOCOL_VERSION +from .. import PROTOCOL_VERSION class ConnectionOptions(object): @@ -173,7 +173,7 @@ class NetworkingThread(threading.Thread): break self.connection._write_lock.release() - # Read and react to as many as 50 packets + # Read and react to as many as 50 packets num_packets = 0 packet = self.connection.reactor.read_packet( self.connection.file_object) @@ -205,8 +205,8 @@ class PacketReactor(object): self.connection = connection def read_packet(self, stream): - ready_to_read, _, __ = select.select([self.connection.socket], [], [], - self.TIME_OUT) + ready_to_read = select.select([self.connection.socket], [], [], + self.TIME_OUT)[0] if self.connection.socket in ready_to_read: length = VarInt.read_socket(self.connection.socket) @@ -278,8 +278,9 @@ class LoginReactor(PacketReactor): decryptor = cipher.decryptor() self.connection.socket = encryption.EncryptedSocketWrapper( self.connection.socket, encryptor, decryptor) - self.connection.file_object = encryption.EncryptedFileObjectWrapper( - self.connection.file_object, decryptor) + self.connection.file_object = \ + encryption.EncryptedFileObjectWrapper( + self.connection.file_object, decryptor) if packet.packet_name == "disconnect": print(packet.json_data) # TODO: handle propagating this back @@ -328,7 +329,7 @@ class StatusReactor(PacketReactor): if packet.id == packets.ResponsePacket.id: import json - print json.loads(packet.json_response) + print(json.loads(packet.json_response)) ping_packet = packets.PingPacket() ping_packet.time = int(time.time()) diff --git a/minecraft/networking/encryption.py b/minecraft/networking/encryption.py index b53051a..53ae646 100644 --- a/minecraft/networking/encryption.py +++ b/minecraft/networking/encryption.py @@ -54,6 +54,11 @@ def minecraft_sha1_hash_digest(sha1_hash): def _number_from_bytes(b, signed=False): + try: + return int.from_bytes(b, byteorder='big', signed=signed) + except AttributeError: + pass + if len(b) == 0: b = b'\x00' num = int(str(b).encode('hex'), 16) diff --git a/minecraft/networking/packets.py b/minecraft/networking/packets.py index 4ada358..c7f75eb 100644 --- a/minecraft/networking/packets.py +++ b/minecraft/networking/packets.py @@ -1,36 +1,37 @@ from io import BytesIO from zlib import compress -from types import * +from .types import ( + VarInt, Integer, Float, Double, UnsignedShort, Long, Byte, UnsignedByte, + String, VarIntPrefixedByteArray, Boolean +) class PacketBuffer(object): - def __init__(self): - self.b = BytesIO() + self.bytes = BytesIO() def send(self, value): """ Writes the given bytes to the buffer, designed to emulate socket.send :param value: The bytes to write """ - self.b.write(value) + self.bytes.write(value) def read(self, length): - return self.b.read(length) + return self.bytes.read(length) def reset(self): - self.b = BytesIO() + self.bytes = BytesIO() def reset_cursor(self): - self.b.seek(0) + self.bytes.seek(0) def get_writable(self): - return self.b.getvalue() + return self.bytes.getvalue() class PacketListener(object): - packets_to_listen = [] def __init__(self, callback, *args): @@ -46,7 +47,6 @@ class PacketListener(object): class Packet(object): - packet_name = "base" id = -0x01 definition = [] @@ -61,7 +61,8 @@ class Packet(object): setattr(self, var_name, value) def write(self, socket, compression_threshold=None): - # buffer the data since we need to know the length of each packet's payload + # buffer the data since we need to know the length of each packet's + # payload packet_buffer = PacketBuffer() # write packet's id right off the bat in the header VarInt.send(self.id, packet_buffer) @@ -125,6 +126,7 @@ class PingPacket(Packet): definition = [ {'time': Long}] + STATE_STATUS_CLIENTBOUND = { 0x00: ResponsePacket, 0x01: PingPacket @@ -143,6 +145,7 @@ class PingPacket(Packet): definition = [ {'time': Long}] + STATE_STATUS_SERVERBOUND = { 0x00: RequestPacket, 0x01: PingPacket @@ -181,6 +184,7 @@ class SetCompressionPacket(Packet): definition = [ {'threshold': VarInt}] + STATE_LOGIN_CLIENTBOUND = { 0x00: DisconnectPacket, 0x01: EncryptionRequestPacket, @@ -203,6 +207,7 @@ class EncryptionResponsePacket(Packet): {'shared_secret': VarIntPrefixedByteArray}, {'verify_token': VarIntPrefixedByteArray}] + STATE_LOGIN_SERVERBOUND = { 0x00: LoginStartPacket, 0x01: EncryptionResponsePacket @@ -295,8 +300,9 @@ class PositionAndLookPacket(Packet): {'pitch': Float}, {'on_ground': Boolean}] + STATE_PLAYING_SERVERBOUND = { 0x00: KeepAlivePacket, 0x01: ChatPacket, 0x06: PositionAndLookPacket -} \ No newline at end of file +} diff --git a/minecraft/networking/types.py b/minecraft/networking/types.py index 51b83f5..af4779f 100644 --- a/minecraft/networking/types.py +++ b/minecraft/networking/types.py @@ -115,24 +115,24 @@ class VarInt(Type): @staticmethod def size(value): - for max_value, size in VARINT_SIZE_TABLE.iteritems(): + for max_value, size in VARINT_SIZE_TABLE.items(): if value < max_value: return size # Maps (maximum integer value -> size of VarInt in bytes) VARINT_SIZE_TABLE = { - 2**7: 1, - 2**14: 2, - 2**21: 3, - 2**28: 4, - 2**35: 5, - 2**42: 6, - 2**49: 7, - 2**56: 8, - 2**63: 9, - 2**70: 10, - 2**77: 11, - 2**84: 12 + 2 ** 7: 1, + 2 ** 14: 2, + 2 ** 21: 3, + 2 ** 28: 4, + 2 ** 35: 5, + 2 ** 42: 6, + 2 ** 49: 7, + 2 ** 56: 8, + 2 ** 63: 9, + 2 ** 70: 10, + 2 ** 77: 11, + 2 ** 84: 12 } @@ -168,9 +168,8 @@ class Double(Type): class ShortPrefixedByteArray(Type): @staticmethod - def read(file_object, length=None): - if length is None: - length = Short.read(file_object) + def read(file_object): + length = Short.read(file_object) return struct.unpack(str(length) + "s", file_object.read(length))[0] @staticmethod @@ -181,9 +180,8 @@ class ShortPrefixedByteArray(Type): class VarIntPrefixedByteArray(Type): @staticmethod - def read(file_object, length=None): - if length is None: - length = VarInt.read(file_object) + def read(file_object): + length = VarInt.read(file_object) return struct.unpack(str(length) + "s", file_object.read(length))[0] @staticmethod @@ -196,10 +194,10 @@ class String(Type): @staticmethod def read(file_object): length = VarInt.read(file_object) - return unicode(file_object.read(length), "utf-8") + return file_object.read(length).decode("utf-8") @staticmethod def send(value, socket): - value = unicode(value).encode('utf-8') + value = str(value).encode('utf-8') VarInt.send(len(value), socket) socket.send(value) diff --git a/tests/test_encryption.py b/tests/test_encryption.py index f8e00b6..9f41761 100644 --- a/tests/test_encryption.py +++ b/tests/test_encryption.py @@ -9,8 +9,7 @@ class Hashing(unittest.TestCase): 'simon': '88e16a1019277b15d58faf0541e11910eb756f6'} def test_hashing(self): - for input_value in self.test_data.iterkeys(): + for input_value, result in self.test_data.items(): sha1_hash = hashlib.sha1() - sha1_hash.update(input_value) - self.assertEquals(minecraft_sha1_hash_digest(sha1_hash), - self.test_data[input_value]) + sha1_hash.update(input_value.encode('utf-8')) + self.assertEquals(minecraft_sha1_hash_digest(sha1_hash), result)