mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-21 17:56:30 +01:00
Merge branch 'v1.16' into master
This commit is contained in:
commit
3c84c2a429
3
.gitignore
vendored
3
.gitignore
vendored
@ -81,5 +81,8 @@ target/
|
|||||||
# sftp configuration file
|
# sftp configuration file
|
||||||
sftp-config.json
|
sftp-config.json
|
||||||
|
|
||||||
|
### Visual Studio
|
||||||
|
.vscode
|
||||||
|
|
||||||
### pyCraft ###
|
### pyCraft ###
|
||||||
credentials
|
credentials
|
||||||
|
@ -3,9 +3,7 @@ python: 3.8
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- python: 2.7
|
- python: pypy3
|
||||||
env: TOX_ENV=py27
|
|
||||||
- python: pypy
|
|
||||||
env: TOX_ENV=pypy
|
env: TOX_ENV=pypy
|
||||||
- python: 3.5
|
- python: 3.5
|
||||||
env: TOX_ENV=py35
|
env: TOX_ENV=py35
|
||||||
|
@ -31,6 +31,7 @@ pyCraft is compatible with the following Minecraft releases:
|
|||||||
* 1.13, 1.13.1, 1.13.2
|
* 1.13, 1.13.1, 1.13.2
|
||||||
* 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4
|
* 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4
|
||||||
* 1.15, 1.15.1, 1.15.2
|
* 1.15, 1.15.1, 1.15.2
|
||||||
|
* 1.16, 1.16.1, 1.16.2
|
||||||
|
|
||||||
In addition, some development snapshots and pre-release versions are supported:
|
In addition, some development snapshots and pre-release versions are supported:
|
||||||
`<minecraft/__init__.py>`_ contains a full list of supported Minecraft versions
|
`<minecraft/__init__.py>`_ contains a full list of supported Minecraft versions
|
||||||
@ -50,7 +51,6 @@ Supported Python versions
|
|||||||
-------------------------
|
-------------------------
|
||||||
pyCraft is compatible with (at least) the following Python implementations:
|
pyCraft is compatible with (at least) the following Python implementations:
|
||||||
|
|
||||||
* Python 2.7
|
|
||||||
* Python 3.5
|
* Python 3.5
|
||||||
* Python 3.6
|
* Python 3.6
|
||||||
* Python 3.7
|
* Python 3.7
|
||||||
@ -61,7 +61,7 @@ Requirements
|
|||||||
------------
|
------------
|
||||||
- `cryptography <https://github.com/pyca/cryptography#cryptography>`_
|
- `cryptography <https://github.com/pyca/cryptography#cryptography>`_
|
||||||
- `requests <http://docs.python-requests.org/en/latest/>`_
|
- `requests <http://docs.python-requests.org/en/latest/>`_
|
||||||
- `future <http://python-future.org/>`_
|
- `PyNBT <https://github.com/TkTech/PyNBT>`_
|
||||||
|
|
||||||
The requirements are also stored in ``setup.py``
|
The requirements are also stored in ``setup.py``
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ with a MineCraft server.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The version number of the most recent pyCraft release.
|
# The version number of the most recent pyCraft release.
|
||||||
__version__ = "0.6.0"
|
__version__ = "0.7.0"
|
||||||
|
|
||||||
# A dict mapping the ID string of each Minecraft version supported by pyCraft
|
# A dict mapping the ID string of each Minecraft version supported by pyCraft
|
||||||
# to the corresponding protocol version number. The ID string of a version is
|
# to the corresponding protocol version number. The ID string of a version is
|
||||||
@ -225,20 +225,84 @@ SUPPORTED_MINECRAFT_VERSIONS = {
|
|||||||
'1.15.2-pre1': 576,
|
'1.15.2-pre1': 576,
|
||||||
'1.15.2-pre2': 577,
|
'1.15.2-pre2': 577,
|
||||||
'1.15.2': 578,
|
'1.15.2': 578,
|
||||||
|
'20w06a': 701,
|
||||||
|
'20w07a': 702,
|
||||||
|
'20w08a': 703,
|
||||||
|
'20w09a': 704,
|
||||||
|
'20w10a': 705,
|
||||||
|
'20w11a': 706,
|
||||||
|
'20w12a': 707,
|
||||||
|
'20w13a': 708,
|
||||||
|
'20w13b': 709,
|
||||||
|
'20w14a': 710,
|
||||||
|
'20w15a': 711,
|
||||||
|
'20w16a': 712,
|
||||||
|
'20w17a': 713,
|
||||||
|
'20w18a': 714,
|
||||||
|
'20w19a': 715,
|
||||||
|
'20w20a': 716,
|
||||||
|
'20w20b': 717,
|
||||||
|
'20w21a': 718,
|
||||||
|
'20w22a': 719,
|
||||||
|
'1.16-pre1': 721,
|
||||||
|
'1.16-pre2': 722,
|
||||||
|
'1.16-pre3': 725,
|
||||||
|
'1.16-pre4': 727,
|
||||||
|
'1.16-pre5': 729,
|
||||||
|
'1.16-pre6': 730,
|
||||||
|
'1.16-pre7': 732,
|
||||||
|
'1.16-pre8': 733,
|
||||||
|
'1.16-rc1': 734,
|
||||||
|
'1.16': 735,
|
||||||
|
'1.16.1': 736,
|
||||||
|
'20w27a': 738,
|
||||||
|
'20w28a': 740,
|
||||||
|
'20w29a': 741,
|
||||||
|
'20w30a': 743,
|
||||||
|
'1.16.2-pre1': 744,
|
||||||
|
'1.16.2-pre2': 746,
|
||||||
|
'1.16.2-pre3': 748,
|
||||||
|
'1.16.2-rc1': 749,
|
||||||
|
'1.16.2-rc2': 750,
|
||||||
|
'1.16.2': 751,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Those Minecraft versions supported by pyCraft which are "release" versions,
|
# Those Minecraft versions supported by pyCraft which are "release" versions,
|
||||||
# i.e. not development snapshots or pre-release versions.
|
# i.e. not development snapshots or pre-release versions.
|
||||||
RELEASE_MINECRAFT_VERSIONS = {
|
RELEASE_MINECRAFT_VERSIONS = {}
|
||||||
vid: protocol for (vid, protocol) in SUPPORTED_MINECRAFT_VERSIONS.items()
|
|
||||||
if __import__('re').match(r'\d+(\.\d+)+$', vid)}
|
|
||||||
|
|
||||||
# The protocol versions of SUPPORTED_MINECRAFT_VERSIONS, without duplicates,
|
# The protocol versions of SUPPORTED_MINECRAFT_VERSIONS, without duplicates,
|
||||||
# in ascending numerical (and hence chronological) order.
|
# in ascending numerical (and hence chronological) order.
|
||||||
SUPPORTED_PROTOCOL_VERSIONS = \
|
SUPPORTED_PROTOCOL_VERSIONS = []
|
||||||
sorted(set(SUPPORTED_MINECRAFT_VERSIONS.values()))
|
|
||||||
|
|
||||||
# The protocol versions of RELEASE_MINECRAFT_VERSIONS, without duplicates,
|
# The protocol versions of RELEASE_MINECRAFT_VERSIONS, without duplicates,
|
||||||
# in ascending numerical (and hence chronological) order.
|
# in ascending numerical (and hence chronological) order.
|
||||||
RELEASE_PROTOCOL_VERSIONS = \
|
RELEASE_PROTOCOL_VERSIONS = []
|
||||||
sorted(set(RELEASE_MINECRAFT_VERSIONS.values()))
|
|
||||||
|
|
||||||
|
def initglobals():
|
||||||
|
'''Init the globals from the SUPPORTED_MINECRAFT_VERSIONS dict
|
||||||
|
|
||||||
|
This allows the SUPPORTED_MINECRAFT_VERSIONS dict to be updated, then the
|
||||||
|
other globals can be updated as well, to allow for dynamic version support.
|
||||||
|
All updates are done by reference to allow this to work else where in the
|
||||||
|
code.
|
||||||
|
'''
|
||||||
|
global RELEASE_MINECRAFT_VERSIONS, SUPPORTED_PROTOCOL_VERSIONS
|
||||||
|
global RELEASE_PROTOCOL_VERSIONS
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
for (vid, protocol) in SUPPORTED_MINECRAFT_VERSIONS.items():
|
||||||
|
if re.match(r"\d+(\.\d+)+$", vid):
|
||||||
|
RELEASE_MINECRAFT_VERSIONS[vid] = protocol
|
||||||
|
if protocol not in RELEASE_PROTOCOL_VERSIONS:
|
||||||
|
RELEASE_PROTOCOL_VERSIONS.append(protocol)
|
||||||
|
if protocol not in SUPPORTED_PROTOCOL_VERSIONS:
|
||||||
|
SUPPORTED_PROTOCOL_VERSIONS.append(protocol)
|
||||||
|
|
||||||
|
SUPPORTED_PROTOCOL_VERSIONS.sort()
|
||||||
|
RELEASE_PROTOCOL_VERSIONS.sort()
|
||||||
|
|
||||||
|
|
||||||
|
initglobals()
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
"""
|
|
||||||
This module stores code used for making pyCraft compatible with
|
|
||||||
both Python2 and Python3 while using the same codebase.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Raw input -> input shenangians
|
|
||||||
# example
|
|
||||||
# > from minecraft.compat import input
|
|
||||||
# > input("asd")
|
|
||||||
|
|
||||||
# Hi, I'm pylint, and sometimes I act silly, at which point my programmer
|
|
||||||
# overlords need to correct me.
|
|
||||||
|
|
||||||
# pylint: disable=undefined-variable,redefined-builtin,invalid-name
|
|
||||||
try:
|
|
||||||
input = raw_input
|
|
||||||
except NameError:
|
|
||||||
input = input
|
|
||||||
|
|
||||||
try:
|
|
||||||
unicode = unicode
|
|
||||||
except NameError:
|
|
||||||
unicode = str
|
|
||||||
# pylint: enable=undefined-variable,redefined-builtin,invalid-name
|
|
@ -1,5 +1,3 @@
|
|||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
import zlib
|
import zlib
|
||||||
@ -11,8 +9,6 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from future.utils import raise_
|
|
||||||
|
|
||||||
from .types import VarInt
|
from .types import VarInt
|
||||||
from .packets import clientbound, serverbound
|
from .packets import clientbound, serverbound
|
||||||
from . import packets
|
from . import packets
|
||||||
@ -495,7 +491,8 @@ class Connection(object):
|
|||||||
|
|
||||||
# If allowed by the final exception handler, re-raise the exception.
|
# If allowed by the final exception handler, re-raise the exception.
|
||||||
if self.handle_exception is None and not caught:
|
if self.handle_exception is None and not caught:
|
||||||
raise_(*exc_info)
|
exc_value, exc_tb = exc_info[1:]
|
||||||
|
raise exc_value.with_traceback(exc_tb)
|
||||||
|
|
||||||
def _version_mismatch(self, server_protocol=None, server_version=None):
|
def _version_mismatch(self, server_protocol=None, server_version=None):
|
||||||
if server_protocol is None:
|
if server_protocol is None:
|
||||||
@ -510,7 +507,10 @@ class Connection(object):
|
|||||||
ss = 'supported, but not allowed for this connection' \
|
ss = 'supported, but not allowed for this connection' \
|
||||||
if server_protocol in SUPPORTED_PROTOCOL_VERSIONS \
|
if server_protocol in SUPPORTED_PROTOCOL_VERSIONS \
|
||||||
else 'not supported'
|
else 'not supported'
|
||||||
raise VersionMismatch("Server's %s is %s." % (vs, ss))
|
err = VersionMismatch("Server's %s is %s." % (vs, ss))
|
||||||
|
err.server_protocol = server_protocol
|
||||||
|
err.server_version = server_version
|
||||||
|
raise err
|
||||||
|
|
||||||
def _handle_exit(self):
|
def _handle_exit(self):
|
||||||
if not self.connected and self.handle_exit is not None:
|
if not self.connected and self.handle_exit is not None:
|
||||||
@ -593,7 +593,8 @@ class NetworkingThread(threading.Thread):
|
|||||||
exc_info = None
|
exc_info = None
|
||||||
|
|
||||||
if exc_info is not None:
|
if exc_info is not None:
|
||||||
raise_(*exc_info)
|
exc_value, exc_tb = exc_info[1:]
|
||||||
|
raise exc_value.with_traceback(exc_tb)
|
||||||
|
|
||||||
|
|
||||||
class PacketReactor(object):
|
class PacketReactor(object):
|
||||||
@ -644,14 +645,16 @@ class PacketReactor(object):
|
|||||||
packet_id = VarInt.read(packet_data)
|
packet_id = VarInt.read(packet_data)
|
||||||
|
|
||||||
# If we know the structure of the packet, attempt to parse it
|
# If we know the structure of the packet, attempt to parse it
|
||||||
# otherwise just skip it
|
# otherwise, just return an instance of the base Packet class.
|
||||||
if packet_id in self.clientbound_packets:
|
if packet_id in self.clientbound_packets:
|
||||||
packet = self.clientbound_packets[packet_id]()
|
packet = self.clientbound_packets[packet_id]()
|
||||||
packet.context = self.connection.context
|
packet.context = self.connection.context
|
||||||
packet.read(packet_data)
|
packet.read(packet_data)
|
||||||
return packet
|
|
||||||
else:
|
else:
|
||||||
return packets.Packet(context=self.connection.context)
|
packet = packets.Packet()
|
||||||
|
packet.context = self.connection.context
|
||||||
|
packet.id = packet_id
|
||||||
|
return packet
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, String, VarIntPrefixedByteArray, TrailingByteArray
|
VarInt, String, VarIntPrefixedByteArray, TrailingByteArray, UUID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -54,9 +54,10 @@ class LoginSuccessPacket(Packet):
|
|||||||
0x02
|
0x02
|
||||||
|
|
||||||
packet_name = "login success"
|
packet_name = "login success"
|
||||||
definition = [
|
get_definition = staticmethod(lambda context: [
|
||||||
{'UUID': String},
|
{'UUID': UUID if context.protocol_version >= 707 else String},
|
||||||
{'Username': String}]
|
{'Username': String}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class SetCompressionPacket(Packet):
|
class SetCompressionPacket(Packet):
|
||||||
|
@ -3,10 +3,9 @@ from minecraft.networking.packets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Integer, FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID,
|
FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID, Short, VarInt,
|
||||||
Short, VarInt, Double, Float, String, Enum, Difficulty, Dimension,
|
Double, Float, String, Enum, Difficulty, Long, Vector, Direction,
|
||||||
GameMode, Long, Vector, Direction, PositionAndLook,
|
PositionAndLook, multi_attribute_alias,
|
||||||
multi_attribute_alias,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from .combat_event_packet import CombatEventPacket
|
from .combat_event_packet import CombatEventPacket
|
||||||
@ -18,6 +17,7 @@ from .block_change_packet import BlockChangePacket, MultiBlockChangePacket
|
|||||||
from .explosion_packet import ExplosionPacket
|
from .explosion_packet import ExplosionPacket
|
||||||
from .sound_effect_packet import SoundEffectPacket
|
from .sound_effect_packet import SoundEffectPacket
|
||||||
from .face_player_packet import FacePlayerPacket
|
from .face_player_packet import FacePlayerPacket
|
||||||
|
from .join_game_and_respawn_packets import JoinGamePacket, RespawnPacket
|
||||||
|
|
||||||
|
|
||||||
# Formerly known as state_playing_clientbound.
|
# Formerly known as state_playing_clientbound.
|
||||||
@ -33,6 +33,8 @@ def get_packets(context):
|
|||||||
DisconnectPacket,
|
DisconnectPacket,
|
||||||
SpawnPlayerPacket,
|
SpawnPlayerPacket,
|
||||||
EntityVelocityPacket,
|
EntityVelocityPacket,
|
||||||
|
EntityPositionDeltaPacket,
|
||||||
|
TimeUpdatePacket,
|
||||||
UpdateHealthPacket,
|
UpdateHealthPacket,
|
||||||
CombatEventPacket,
|
CombatEventPacket,
|
||||||
ExplosionPacket,
|
ExplosionPacket,
|
||||||
@ -62,7 +64,9 @@ def get_packets(context):
|
|||||||
class KeepAlivePacket(AbstractKeepAlivePacket):
|
class KeepAlivePacket(AbstractKeepAlivePacket):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x21 if context.protocol_version >= 550 else \
|
return 0x1F if context.protocol_version >= 741 else \
|
||||||
|
0x20 if context.protocol_version >= 721 else \
|
||||||
|
0x21 if context.protocol_version >= 550 else \
|
||||||
0x20 if context.protocol_version >= 471 else \
|
0x20 if context.protocol_version >= 471 else \
|
||||||
0x21 if context.protocol_version >= 389 else \
|
0x21 if context.protocol_version >= 389 else \
|
||||||
0x20 if context.protocol_version >= 345 else \
|
0x20 if context.protocol_version >= 345 else \
|
||||||
@ -72,41 +76,11 @@ class KeepAlivePacket(AbstractKeepAlivePacket):
|
|||||||
0x00
|
0x00
|
||||||
|
|
||||||
|
|
||||||
class JoinGamePacket(Packet):
|
|
||||||
@staticmethod
|
|
||||||
def get_id(context):
|
|
||||||
return 0x26 if context.protocol_version >= 550 else \
|
|
||||||
0x25 if context.protocol_version >= 389 else \
|
|
||||||
0x24 if context.protocol_version >= 345 else \
|
|
||||||
0x23 if context.protocol_version >= 332 else \
|
|
||||||
0x24 if context.protocol_version >= 318 else \
|
|
||||||
0x23 if context.protocol_version >= 107 else \
|
|
||||||
0x01
|
|
||||||
|
|
||||||
packet_name = "join game"
|
|
||||||
get_definition = staticmethod(lambda context: [
|
|
||||||
{'entity_id': Integer},
|
|
||||||
{'game_mode': UnsignedByte},
|
|
||||||
{'dimension': Integer if context.protocol_version >= 108 else Byte},
|
|
||||||
{'hashed_seed': Long} if context.protocol_version >= 552 else {},
|
|
||||||
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {},
|
|
||||||
{'max_players': UnsignedByte},
|
|
||||||
{'level_type': String},
|
|
||||||
{'render_distance': VarInt} if context.protocol_version >= 468 else {},
|
|
||||||
{'reduced_debug_info': Boolean},
|
|
||||||
{'respawn_screen': Boolean} if context.protocol_version >= 571 else {},
|
|
||||||
])
|
|
||||||
|
|
||||||
# These aliases declare the Enum type corresponding to each field:
|
|
||||||
Difficulty = Difficulty
|
|
||||||
GameMode = GameMode
|
|
||||||
Dimension = Dimension
|
|
||||||
|
|
||||||
|
|
||||||
class ServerDifficultyPacket(Packet):
|
class ServerDifficultyPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x0E if context.protocol_version >= 550 else \
|
return 0x0D if context.protocol_version >= 721 else \
|
||||||
|
0x0E if context.protocol_version >= 550 else \
|
||||||
0x0D if context.protocol_version >= 332 else \
|
0x0D if context.protocol_version >= 332 else \
|
||||||
0x0E if context.protocol_version >= 318 else \
|
0x0E if context.protocol_version >= 318 else \
|
||||||
0x0D if context.protocol_version >= 70 else \
|
0x0D if context.protocol_version >= 70 else \
|
||||||
@ -125,7 +99,8 @@ class ServerDifficultyPacket(Packet):
|
|||||||
class ChatMessagePacket(Packet):
|
class ChatMessagePacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x0F if context.protocol_version >= 550 else \
|
return 0x0E if context.protocol_version >= 721 else \
|
||||||
|
0x0F if context.protocol_version >= 550 else \
|
||||||
0x0E if context.protocol_version >= 343 else \
|
0x0E if context.protocol_version >= 343 else \
|
||||||
0x0F if context.protocol_version >= 332 else \
|
0x0F if context.protocol_version >= 332 else \
|
||||||
0x10 if context.protocol_version >= 317 else \
|
0x10 if context.protocol_version >= 317 else \
|
||||||
@ -133,9 +108,11 @@ class ChatMessagePacket(Packet):
|
|||||||
0x02
|
0x02
|
||||||
|
|
||||||
packet_name = "chat message"
|
packet_name = "chat message"
|
||||||
definition = [
|
get_definition = staticmethod(lambda context: [
|
||||||
{'json_data': String},
|
{'json_data': String},
|
||||||
{'position': Byte}]
|
{'position': Byte},
|
||||||
|
{'sender': UUID} if context.protocol_version >= 718 else {},
|
||||||
|
])
|
||||||
|
|
||||||
class Position(Enum):
|
class Position(Enum):
|
||||||
CHAT = 0 # A player-initiated chat message.
|
CHAT = 0 # A player-initiated chat message.
|
||||||
@ -146,7 +123,9 @@ class ChatMessagePacket(Packet):
|
|||||||
class DisconnectPacket(Packet):
|
class DisconnectPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x1B if context.protocol_version >= 550 else \
|
return 0x19 if context.protocol_version >= 741 else \
|
||||||
|
0x1A if context.protocol_version >= 721 else \
|
||||||
|
0x1B if context.protocol_version >= 550 else \
|
||||||
0x1A if context.protocol_version >= 471 else \
|
0x1A if context.protocol_version >= 471 else \
|
||||||
0x1B if context.protocol_version >= 345 else \
|
0x1B if context.protocol_version >= 345 else \
|
||||||
0x1A if context.protocol_version >= 332 else \
|
0x1A if context.protocol_version >= 332 else \
|
||||||
@ -171,7 +150,8 @@ class SetCompressionPacket(Packet):
|
|||||||
class SpawnPlayerPacket(Packet):
|
class SpawnPlayerPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x05 if context.protocol_version >= 67 else \
|
return 0x04 if context.protocol_version >= 721 else \
|
||||||
|
0x05 if context.protocol_version >= 67 else \
|
||||||
0x0C
|
0x0C
|
||||||
|
|
||||||
packet_name = 'spawn player'
|
packet_name = 'spawn player'
|
||||||
@ -206,7 +186,9 @@ class SpawnPlayerPacket(Packet):
|
|||||||
class EntityVelocityPacket(Packet):
|
class EntityVelocityPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x46 if context.protocol_version >= 550 else \
|
return 0x46 if context.protocol_version >= 721 else \
|
||||||
|
0x47 if context.protocol_version >= 707 else \
|
||||||
|
0x46 if context.protocol_version >= 550 else \
|
||||||
0x45 if context.protocol_version >= 471 else \
|
0x45 if context.protocol_version >= 471 else \
|
||||||
0x41 if context.protocol_version >= 461 else \
|
0x41 if context.protocol_version >= 461 else \
|
||||||
0x42 if context.protocol_version >= 451 else \
|
0x42 if context.protocol_version >= 451 else \
|
||||||
@ -229,10 +211,59 @@ class EntityVelocityPacket(Packet):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class EntityPositionDeltaPacket(Packet):
|
||||||
|
@staticmethod
|
||||||
|
def get_id(context):
|
||||||
|
return 0x27 if context.protocol_version >= 741 else \
|
||||||
|
0x28 if context.protocol_version >= 721 else \
|
||||||
|
0x29 if context.protocol_version >= 550 else \
|
||||||
|
0x28 if context.protocol_version >= 389 else \
|
||||||
|
0x27 if context.protocol_version >= 345 else \
|
||||||
|
0x26 if context.protocol_version >= 318 else \
|
||||||
|
0x25 if context.protocol_version >= 94 else \
|
||||||
|
0x26 if context.protocol_version >= 70 else \
|
||||||
|
0x15
|
||||||
|
|
||||||
|
packet_name = "entity position delta"
|
||||||
|
get_definition = staticmethod(lambda context: [
|
||||||
|
{'entity_id': VarInt},
|
||||||
|
{'delta_x': Short},
|
||||||
|
{'delta_y': Short},
|
||||||
|
{'delta_z': Short},
|
||||||
|
{'on_ground': Boolean}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class TimeUpdatePacket(Packet):
|
||||||
|
@staticmethod
|
||||||
|
def get_id(context):
|
||||||
|
return 0x4E if context.protocol_version >= 721 else \
|
||||||
|
0x4F if context.protocol_version >= 550 else \
|
||||||
|
0x4E if context.protocol_version >= 471 else \
|
||||||
|
0x4A if context.protocol_version >= 461 else \
|
||||||
|
0x4B if context.protocol_version >= 451 else \
|
||||||
|
0x4A if context.protocol_version >= 389 else \
|
||||||
|
0x49 if context.protocol_version >= 352 else \
|
||||||
|
0x48 if context.protocol_version >= 345 else \
|
||||||
|
0x47 if context.protocol_version >= 336 else \
|
||||||
|
0x46 if context.protocol_version >= 318 else \
|
||||||
|
0x44 if context.protocol_version >= 94 else \
|
||||||
|
0x43 if context.protocol_version >= 70 else \
|
||||||
|
0x03
|
||||||
|
|
||||||
|
packet_name = "time update"
|
||||||
|
get_definition = staticmethod(lambda context: [
|
||||||
|
{'world_age': Long},
|
||||||
|
{'time_of_day': Long},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class UpdateHealthPacket(Packet):
|
class UpdateHealthPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x49 if context.protocol_version >= 550 else \
|
return 0x49 if context.protocol_version >= 721 else \
|
||||||
|
0x4A if context.protocol_version >= 707 else \
|
||||||
|
0x49 if context.protocol_version >= 550 else \
|
||||||
0x48 if context.protocol_version >= 471 else \
|
0x48 if context.protocol_version >= 471 else \
|
||||||
0x44 if context.protocol_version >= 461 else \
|
0x44 if context.protocol_version >= 461 else \
|
||||||
0x45 if context.protocol_version >= 451 else \
|
0x45 if context.protocol_version >= 451 else \
|
||||||
@ -254,41 +285,12 @@ class UpdateHealthPacket(Packet):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class RespawnPacket(Packet):
|
|
||||||
@staticmethod
|
|
||||||
def get_id(context):
|
|
||||||
return 0x3B if context.protocol_version >= 550 else \
|
|
||||||
0x3A if context.protocol_version >= 471 else \
|
|
||||||
0x38 if context.protocol_version >= 461 else \
|
|
||||||
0x39 if context.protocol_version >= 451 else \
|
|
||||||
0x38 if context.protocol_version >= 389 else \
|
|
||||||
0x37 if context.protocol_version >= 352 else \
|
|
||||||
0x36 if context.protocol_version >= 345 else \
|
|
||||||
0x35 if context.protocol_version >= 336 else \
|
|
||||||
0x34 if context.protocol_version >= 332 else \
|
|
||||||
0x35 if context.protocol_version >= 318 else \
|
|
||||||
0x33 if context.protocol_version >= 70 else \
|
|
||||||
0x07
|
|
||||||
|
|
||||||
packet_name = 'respawn'
|
|
||||||
get_definition = staticmethod(lambda context: [
|
|
||||||
{'dimension': Integer},
|
|
||||||
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {},
|
|
||||||
{'hashed_seed': Long} if context.protocol_version >= 552 else {},
|
|
||||||
{'game_mode': UnsignedByte},
|
|
||||||
{'level_type': String},
|
|
||||||
])
|
|
||||||
|
|
||||||
# These aliases declare the Enum type corresponding to each field:
|
|
||||||
Difficulty = Difficulty
|
|
||||||
Dimension = Dimension
|
|
||||||
GameMode = GameMode
|
|
||||||
|
|
||||||
|
|
||||||
class PluginMessagePacket(AbstractPluginMessagePacket):
|
class PluginMessagePacket(AbstractPluginMessagePacket):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x19 if context.protocol_version >= 550 else \
|
return 0x17 if context.protocol_version >= 741 else \
|
||||||
|
0x18 if context.protocol_version >= 721 else \
|
||||||
|
0x19 if context.protocol_version >= 550 else \
|
||||||
0x18 if context.protocol_version >= 471 else \
|
0x18 if context.protocol_version >= 471 else \
|
||||||
0x19 if context.protocol_version >= 345 else \
|
0x19 if context.protocol_version >= 345 else \
|
||||||
0x18 if context.protocol_version >= 332 else \
|
0x18 if context.protocol_version >= 332 else \
|
||||||
@ -300,7 +302,8 @@ class PluginMessagePacket(AbstractPluginMessagePacket):
|
|||||||
class PlayerListHeaderAndFooterPacket(Packet):
|
class PlayerListHeaderAndFooterPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x54 if context.protocol_version >= 550 else \
|
return 0x53 if context.protocol_version >= 721 else \
|
||||||
|
0x54 if context.protocol_version >= 550 else \
|
||||||
0x53 if context.protocol_version >= 471 else \
|
0x53 if context.protocol_version >= 471 else \
|
||||||
0x5F if context.protocol_version >= 461 else \
|
0x5F if context.protocol_version >= 461 else \
|
||||||
0x50 if context.protocol_version >= 451 else \
|
0x50 if context.protocol_version >= 451 else \
|
||||||
@ -321,7 +324,9 @@ class PlayerListHeaderAndFooterPacket(Packet):
|
|||||||
class EntityLookPacket(Packet):
|
class EntityLookPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x2B if context.protocol_version >= 550 else \
|
return 0x29 if context.protocol_version >= 741 else \
|
||||||
|
0x2A if context.protocol_version >= 721 else \
|
||||||
|
0x2B if context.protocol_version >= 550 else \
|
||||||
0x2A if context.protocol_version >= 389 else \
|
0x2A if context.protocol_version >= 389 else \
|
||||||
0x29 if context.protocol_version >= 345 else \
|
0x29 if context.protocol_version >= 345 else \
|
||||||
0x28 if context.protocol_version >= 318 else \
|
0x28 if context.protocol_version >= 318 else \
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Integer, UnsignedByte, Position, Vector, MutableRecord,
|
Type, VarInt, VarLong, Long, Integer, UnsignedByte, Position, Vector,
|
||||||
attribute_alias, multi_attribute_alias,
|
MutableRecord, PrefixedArray, Boolean, attribute_alias,
|
||||||
|
multi_attribute_alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BlockChangePacket(Packet):
|
class BlockChangePacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x0C if context.protocol_version >= 550 else \
|
return 0x0B if context.protocol_version >= 721 else \
|
||||||
|
0x0C if context.protocol_version >= 550 else \
|
||||||
0x0B if context.protocol_version >= 332 else \
|
0x0B if context.protocol_version >= 332 else \
|
||||||
0x0C if context.protocol_version >= 318 else \
|
0x0C if context.protocol_version >= 318 else \
|
||||||
0x0B if context.protocol_version >= 67 else \
|
0x0B if context.protocol_version >= 67 else \
|
||||||
@ -46,7 +48,9 @@ class BlockChangePacket(Packet):
|
|||||||
class MultiBlockChangePacket(Packet):
|
class MultiBlockChangePacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x10 if context.protocol_version >= 550 else \
|
return 0x3B if context.protocol_version >= 741 else \
|
||||||
|
0x0F if context.protocol_version >= 721 else \
|
||||||
|
0x10 if context.protocol_version >= 550 else \
|
||||||
0x0F if context.protocol_version >= 343 else \
|
0x0F if context.protocol_version >= 343 else \
|
||||||
0x10 if context.protocol_version >= 332 else \
|
0x10 if context.protocol_version >= 332 else \
|
||||||
0x11 if context.protocol_version >= 318 else \
|
0x11 if context.protocol_version >= 318 else \
|
||||||
@ -55,12 +59,23 @@ class MultiBlockChangePacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'multi block change'
|
packet_name = 'multi block change'
|
||||||
|
|
||||||
fields = 'chunk_x', 'chunk_z', 'records'
|
# Only used in protocol 741 and later.
|
||||||
|
class ChunkSectionPos(Vector, Type):
|
||||||
|
@classmethod
|
||||||
|
def read(cls, file_object):
|
||||||
|
value = Long.read(file_object)
|
||||||
|
x = value >> 42
|
||||||
|
z = (value >> 20) & 0x3FFFFF
|
||||||
|
y = value & 0xFFFFF
|
||||||
|
return cls(x, y, z)
|
||||||
|
|
||||||
# Access the 'chunk_x' and 'chunk_z' fields as a tuple.
|
@classmethod
|
||||||
chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z')
|
def send(cls, pos, socket):
|
||||||
|
x, y, z = pos
|
||||||
|
value = (x & 0x3FFFFF) << 42 | (z & 0x3FFFFF) << 20 | y & 0xFFFFF
|
||||||
|
Long.send(value, socket)
|
||||||
|
|
||||||
class Record(MutableRecord):
|
class Record(MutableRecord, Type):
|
||||||
__slots__ = 'x', 'y', 'z', 'block_state_id'
|
__slots__ = 'x', 'y', 'z', 'block_state_id'
|
||||||
|
|
||||||
def __init__(self, **kwds):
|
def __init__(self, **kwds):
|
||||||
@ -91,30 +106,47 @@ class MultiBlockChangePacket(Packet):
|
|||||||
# This alias is retained for backward compatibility.
|
# This alias is retained for backward compatibility.
|
||||||
blockStateId = attribute_alias('block_state_id')
|
blockStateId = attribute_alias('block_state_id')
|
||||||
|
|
||||||
def read(self, file_object):
|
@classmethod
|
||||||
|
def read_with_context(cls, file_object, context):
|
||||||
|
record = cls()
|
||||||
|
if context.protocol_version >= 741:
|
||||||
|
value = VarLong.read(file_object)
|
||||||
|
record.block_state_id = value >> 12
|
||||||
|
record.x = (value >> 8) & 0xF
|
||||||
|
record.z = (value >> 4) & 0xF
|
||||||
|
record.y = value & 0xF
|
||||||
|
else:
|
||||||
h_position = UnsignedByte.read(file_object)
|
h_position = UnsignedByte.read(file_object)
|
||||||
self.x, self.z = h_position >> 4, h_position & 0xF
|
record.x = h_position >> 4
|
||||||
self.y = UnsignedByte.read(file_object)
|
record.z = h_position & 0xF
|
||||||
self.block_state_id = VarInt.read(file_object)
|
record.y = UnsignedByte.read(file_object)
|
||||||
|
record.block_state_id = VarInt.read(file_object)
|
||||||
|
return record
|
||||||
|
|
||||||
def write(self, packet_buffer):
|
@classmethod
|
||||||
UnsignedByte.send(self.x << 4 | self.z & 0xF, packet_buffer)
|
def send_with_context(self, record, socket, context):
|
||||||
UnsignedByte.send(self.y, packet_buffer)
|
if context.protocol_version >= 741:
|
||||||
VarInt.send(self.block_state_id, packet_buffer)
|
value = record.block_state_id << 12 | \
|
||||||
|
(record.x & 0xF) << 8 | \
|
||||||
|
(record.z & 0xF) << 4 | \
|
||||||
|
record.y & 0xF
|
||||||
|
VarLong.send(value, socket)
|
||||||
|
else:
|
||||||
|
UnsignedByte.send(record.x << 4 | record.z & 0xF, socket)
|
||||||
|
UnsignedByte.send(record.y, socket)
|
||||||
|
VarInt.send(record.block_state_id, socket)
|
||||||
|
|
||||||
def read(self, file_object):
|
get_definition = staticmethod(lambda context: [
|
||||||
self.chunk_x = Integer.read(file_object)
|
{'chunk_section_pos': MultiBlockChangePacket.ChunkSectionPos},
|
||||||
self.chunk_z = Integer.read(file_object)
|
{'invert_trust_edges': Boolean}
|
||||||
records_count = VarInt.read(file_object)
|
if context.protocol_version >= 748 else {}, # Provisional field name.
|
||||||
self.records = []
|
{'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)},
|
||||||
for i in range(records_count):
|
] if context.protocol_version >= 741 else [
|
||||||
record = self.Record()
|
{'chunk_x': Integer},
|
||||||
record.read(file_object)
|
{'chunk_z': Integer},
|
||||||
self.records.append(record)
|
{'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)},
|
||||||
|
])
|
||||||
|
|
||||||
def write_fields(self, packet_buffer):
|
# Access the 'chunk_x' and 'chunk_z' fields as a tuple.
|
||||||
Integer.send(self.chunk_x, packet_buffer)
|
# Only used prior to protocol 741.
|
||||||
Integer.send(self.chunk_z, packet_buffer)
|
chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z')
|
||||||
VarInt.send(len(self.records), packet_buffer)
|
|
||||||
for record in self.records:
|
|
||||||
record.write(packet_buffer)
|
|
||||||
|
@ -8,7 +8,9 @@ from minecraft.networking.types import (
|
|||||||
class CombatEventPacket(Packet):
|
class CombatEventPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x33 if context.protocol_version >= 550 else \
|
return 0x31 if context.protocol_version >= 741 else \
|
||||||
|
0x32 if context.protocol_version >= 721 else \
|
||||||
|
0x33 if context.protocol_version >= 550 else \
|
||||||
0x32 if context.protocol_version >= 471 else \
|
0x32 if context.protocol_version >= 471 else \
|
||||||
0x30 if context.protocol_version >= 451 else \
|
0x30 if context.protocol_version >= 451 else \
|
||||||
0x2F if context.protocol_version >= 389 else \
|
0x2F if context.protocol_version >= 389 else \
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Vector, Float, Byte, Integer, multi_attribute_alias,
|
Vector, Float, Byte, Integer, PrefixedArray, multi_attribute_alias, Type,
|
||||||
)
|
)
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
@ -7,7 +7,9 @@ from minecraft.networking.packets import Packet
|
|||||||
class ExplosionPacket(Packet):
|
class ExplosionPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x1D if context.protocol_version >= 550 else \
|
return 0x1B if context.protocol_version >= 741 else \
|
||||||
|
0x1C if context.protocol_version >= 721 else \
|
||||||
|
0x1D if context.protocol_version >= 550 else \
|
||||||
0x1C if context.protocol_version >= 471 else \
|
0x1C if context.protocol_version >= 471 else \
|
||||||
0x1E if context.protocol_version >= 389 else \
|
0x1E if context.protocol_version >= 389 else \
|
||||||
0x1D if context.protocol_version >= 345 else \
|
0x1D if context.protocol_version >= 345 else \
|
||||||
@ -19,8 +21,27 @@ class ExplosionPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'explosion'
|
packet_name = 'explosion'
|
||||||
|
|
||||||
fields = 'x', 'y', 'z', 'radius', 'records', \
|
class Record(Vector, Type):
|
||||||
'player_motion_x', 'player_motion_y', 'player_motion_z'
|
__slots__ = ()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def read(cls, file_object):
|
||||||
|
return cls(*(Byte.read(file_object) for i in range(3)))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def send(cls, record, socket):
|
||||||
|
for coord in record:
|
||||||
|
Byte.send(coord, socket)
|
||||||
|
|
||||||
|
definition = [
|
||||||
|
{'x': Float},
|
||||||
|
{'y': Float},
|
||||||
|
{'z': Float},
|
||||||
|
{'radius': Float},
|
||||||
|
{'records': PrefixedArray(Integer, Record)},
|
||||||
|
{'player_motion_x': Float},
|
||||||
|
{'player_motion_y': Float},
|
||||||
|
{'player_motion_z': Float}]
|
||||||
|
|
||||||
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
||||||
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
@ -28,37 +49,3 @@ class ExplosionPacket(Packet):
|
|||||||
# Access the 'player_motion_{x,y,z}' fields as a Vector tuple.
|
# Access the 'player_motion_{x,y,z}' fields as a Vector tuple.
|
||||||
player_motion = multi_attribute_alias(
|
player_motion = multi_attribute_alias(
|
||||||
Vector, 'player_motion_x', 'player_motion_y', 'player_motion_z')
|
Vector, 'player_motion_x', 'player_motion_y', 'player_motion_z')
|
||||||
|
|
||||||
class Record(Vector):
|
|
||||||
__slots__ = ()
|
|
||||||
|
|
||||||
def read(self, file_object):
|
|
||||||
self.x = Float.read(file_object)
|
|
||||||
self.y = Float.read(file_object)
|
|
||||||
self.z = Float.read(file_object)
|
|
||||||
self.radius = Float.read(file_object)
|
|
||||||
records_count = Integer.read(file_object)
|
|
||||||
self.records = []
|
|
||||||
for i in range(records_count):
|
|
||||||
rec_x = Byte.read(file_object)
|
|
||||||
rec_y = Byte.read(file_object)
|
|
||||||
rec_z = Byte.read(file_object)
|
|
||||||
record = ExplosionPacket.Record(rec_x, rec_y, rec_z)
|
|
||||||
self.records.append(record)
|
|
||||||
self.player_motion_x = Float.read(file_object)
|
|
||||||
self.player_motion_y = Float.read(file_object)
|
|
||||||
self.player_motion_z = Float.read(file_object)
|
|
||||||
|
|
||||||
def write_fields(self, packet_buffer):
|
|
||||||
Float.send(self.x, packet_buffer)
|
|
||||||
Float.send(self.y, packet_buffer)
|
|
||||||
Float.send(self.z, packet_buffer)
|
|
||||||
Float.send(self.radius, packet_buffer)
|
|
||||||
Integer.send(len(self.records), packet_buffer)
|
|
||||||
for record in self.records:
|
|
||||||
Byte.send(record.x, packet_buffer)
|
|
||||||
Byte.send(record.y, packet_buffer)
|
|
||||||
Byte.send(record.z, packet_buffer)
|
|
||||||
Float.send(self.player_motion_x, packet_buffer)
|
|
||||||
Float.send(self.player_motion_y, packet_buffer)
|
|
||||||
Float.send(self.player_motion_z, packet_buffer)
|
|
||||||
|
@ -8,7 +8,9 @@ from minecraft.networking.packets import Packet
|
|||||||
class FacePlayerPacket(Packet):
|
class FacePlayerPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x35 if context.protocol_version >= 550 else \
|
return 0x33 if context.protocol_version >= 741 else \
|
||||||
|
0x34 if context.protocol_version >= 721 else \
|
||||||
|
0x35 if context.protocol_version >= 550 else \
|
||||||
0x34 if context.protocol_version >= 471 else \
|
0x34 if context.protocol_version >= 471 else \
|
||||||
0x32 if context.protocol_version >= 451 else \
|
0x32 if context.protocol_version >= 451 else \
|
||||||
0x31 if context.protocol_version >= 389 else \
|
0x31 if context.protocol_version >= 389 else \
|
||||||
|
@ -0,0 +1,208 @@
|
|||||||
|
import pynbt
|
||||||
|
|
||||||
|
from minecraft.networking.packets import Packet
|
||||||
|
from minecraft.networking.types import (
|
||||||
|
NBT, Integer, Boolean, UnsignedByte, String, Byte, Long, VarInt,
|
||||||
|
PrefixedArray, Difficulty, GameMode, Dimension,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def nbt_to_snbt(tag):
|
||||||
|
'''Convert a pyNBT tag to SNBT ("stringified NBT") format.'''
|
||||||
|
scalars = {
|
||||||
|
pynbt.TAG_Byte: 'b',
|
||||||
|
pynbt.TAG_Short: 's',
|
||||||
|
pynbt.TAG_Int: '',
|
||||||
|
pynbt.TAG_Long: 'l',
|
||||||
|
pynbt.TAG_Float: 'f',
|
||||||
|
pynbt.TAG_Double: 'd',
|
||||||
|
}
|
||||||
|
if type(tag) in scalars:
|
||||||
|
return repr(tag.value) + scalars[type(tag)]
|
||||||
|
|
||||||
|
arrays = {
|
||||||
|
pynbt.TAG_Byte_Array: 'B',
|
||||||
|
pynbt.TAG_Int_Array: 'I',
|
||||||
|
pynbt.TAG_Long_Array: 'L',
|
||||||
|
}
|
||||||
|
if type(tag) in arrays:
|
||||||
|
return '[' + arrays[type(tag)] + ';' + \
|
||||||
|
','.join(map(repr, tag.value)) + ']'
|
||||||
|
|
||||||
|
if isinstance(tag, pynbt.TAG_String):
|
||||||
|
return repr(tag.value)
|
||||||
|
|
||||||
|
if isinstance(tag, pynbt.TAG_List):
|
||||||
|
return '[' + ','.join(map(nbt_to_snbt, tag.value)) + ']'
|
||||||
|
|
||||||
|
if isinstance(tag, pynbt.TAG_Compound):
|
||||||
|
return '{' + ','.join(n + ':' + nbt_to_snbt(v)
|
||||||
|
for (n, v) in tag.items()) + '}'
|
||||||
|
|
||||||
|
raise TypeError('Unknown NBT tag type: %r' % type(tag))
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractDimensionPacket(Packet):
|
||||||
|
''' The abstract superclass of JoinGamePacket and RespawnPacket, containing
|
||||||
|
common definitions relating to their 'dimension' field.
|
||||||
|
'''
|
||||||
|
def field_string(self, field):
|
||||||
|
# pylint: disable=no-member
|
||||||
|
if self.context.protocol_version >= 748 and field == 'dimension':
|
||||||
|
return nbt_to_snbt(self.dimension)
|
||||||
|
elif self.context.protocol_version < 718 and field == 'dimension':
|
||||||
|
return Dimension.name_from_value(self.dimension)
|
||||||
|
return super(AbstractDimensionPacket, self).field_string(field)
|
||||||
|
|
||||||
|
|
||||||
|
class JoinGamePacket(AbstractDimensionPacket):
|
||||||
|
@staticmethod
|
||||||
|
def get_id(context):
|
||||||
|
return 0x24 if context.protocol_version >= 741 else \
|
||||||
|
0x25 if context.protocol_version >= 721 else \
|
||||||
|
0x26 if context.protocol_version >= 550 else \
|
||||||
|
0x25 if context.protocol_version >= 389 else \
|
||||||
|
0x24 if context.protocol_version >= 345 else \
|
||||||
|
0x23 if context.protocol_version >= 332 else \
|
||||||
|
0x24 if context.protocol_version >= 318 else \
|
||||||
|
0x23 if context.protocol_version >= 107 else \
|
||||||
|
0x01
|
||||||
|
|
||||||
|
packet_name = "join game"
|
||||||
|
get_definition = staticmethod(lambda context: [
|
||||||
|
{'entity_id': Integer},
|
||||||
|
{'is_hardcore': Boolean} if context.protocol_version >= 738 else {},
|
||||||
|
{'game_mode': UnsignedByte},
|
||||||
|
{'previous_game_mode': UnsignedByte}
|
||||||
|
if context.protocol_version >= 730 else {},
|
||||||
|
{'world_names': PrefixedArray(VarInt, String)}
|
||||||
|
if context.protocol_version >= 722 else {},
|
||||||
|
{'dimension_codec': NBT}
|
||||||
|
if context.protocol_version >= 718 else {},
|
||||||
|
{'dimension':
|
||||||
|
NBT if context.protocol_version >= 748 else
|
||||||
|
String if context.protocol_version >= 718 else
|
||||||
|
Integer if context.protocol_version >= 108 else
|
||||||
|
Byte},
|
||||||
|
{'world_name': String} if context.protocol_version >= 722 else {},
|
||||||
|
{'hashed_seed': Long} if context.protocol_version >= 552 else {},
|
||||||
|
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {},
|
||||||
|
{'max_players':
|
||||||
|
VarInt if context.protocol_version >= 749 else UnsignedByte},
|
||||||
|
{'level_type': String} if context.protocol_version < 716 else {},
|
||||||
|
{'render_distance': VarInt} if context.protocol_version >= 468 else {},
|
||||||
|
{'reduced_debug_info': Boolean},
|
||||||
|
{'respawn_screen': Boolean} if context.protocol_version >= 571 else {},
|
||||||
|
{'is_debug': Boolean} if context.protocol_version >= 716 else {},
|
||||||
|
{'is_flat': Boolean} if context.protocol_version >= 716 else {},
|
||||||
|
])
|
||||||
|
|
||||||
|
# These aliases declare the Enum type corresponding to each field:
|
||||||
|
Difficulty = Difficulty
|
||||||
|
GameMode = GameMode
|
||||||
|
|
||||||
|
# Accesses the 'game_mode' field appropriately depending on the protocol.
|
||||||
|
# Can be set or deleted when 'context' is undefined.
|
||||||
|
@property
|
||||||
|
def game_mode(self):
|
||||||
|
if self.context.protocol_version >= 738:
|
||||||
|
return self._game_mode_738
|
||||||
|
else:
|
||||||
|
return self._game_mode_0
|
||||||
|
|
||||||
|
@game_mode.setter
|
||||||
|
def game_mode(self, value):
|
||||||
|
self._game_mode_738 = value
|
||||||
|
self._game_mode_0 = value
|
||||||
|
|
||||||
|
@game_mode.deleter
|
||||||
|
def game_mode(self):
|
||||||
|
del self._game_mode_738
|
||||||
|
del self._game_mode_0
|
||||||
|
|
||||||
|
# Accesses the 'is_hardcore' field, or its equivalent in older protocols.
|
||||||
|
# Can be set or deleted when 'context' is undefined.
|
||||||
|
@property
|
||||||
|
def is_hardcore(self):
|
||||||
|
if self.context.protocol_version >= 738:
|
||||||
|
return self._is_hardcore
|
||||||
|
else:
|
||||||
|
return bool(self._game_mode_0 & GameMode.HARDCORE)
|
||||||
|
|
||||||
|
@is_hardcore.setter
|
||||||
|
def is_hardcore(self, value):
|
||||||
|
self._is_hardcore = value
|
||||||
|
self._game_mode_0 = \
|
||||||
|
getattr(self, '_game_mode_0', 0) | GameMode.HARDCORE \
|
||||||
|
if value else \
|
||||||
|
getattr(self, '_game_mode_0', 0) & ~GameMode.HARDCORE
|
||||||
|
|
||||||
|
@is_hardcore.deleter
|
||||||
|
def is_hardcore(self):
|
||||||
|
if hasattr(self, '_is_hardcore'):
|
||||||
|
del self._is_hardcore
|
||||||
|
if hasattr(self, '_game_mode_0'):
|
||||||
|
self._game_mode_0 &= ~GameMode.HARDCORE
|
||||||
|
|
||||||
|
# Accesses the component of the 'game_mode' field without any hardcore bit,
|
||||||
|
# version-independently. Can be set or deleted when 'context' is undefined.
|
||||||
|
@property
|
||||||
|
def pure_game_mode(self):
|
||||||
|
if self.context.protocol_version >= 738:
|
||||||
|
return self._game_mode_738
|
||||||
|
else:
|
||||||
|
return self._game_mode_0 & ~GameMode.HARDCORE
|
||||||
|
|
||||||
|
@pure_game_mode.setter
|
||||||
|
def pure_game_mode(self, value):
|
||||||
|
self._game_mode_738 = value
|
||||||
|
self._game_mode_0 = \
|
||||||
|
value & ~GameMode.HARDCORE | \
|
||||||
|
getattr(self, '_game_mode_0', 0) & GameMode.HARDCORE
|
||||||
|
|
||||||
|
def field_string(self, field):
|
||||||
|
if field == 'dimension_codec':
|
||||||
|
# pylint: disable=no-member
|
||||||
|
return nbt_to_snbt(self.dimension_codec)
|
||||||
|
return super(JoinGamePacket, self).field_string(field)
|
||||||
|
|
||||||
|
|
||||||
|
class RespawnPacket(AbstractDimensionPacket):
|
||||||
|
@staticmethod
|
||||||
|
def get_id(context):
|
||||||
|
return 0x39 if context.protocol_version >= 741 else \
|
||||||
|
0x3A if context.protocol_version >= 721 else \
|
||||||
|
0x3B if context.protocol_version >= 550 else \
|
||||||
|
0x3A if context.protocol_version >= 471 else \
|
||||||
|
0x38 if context.protocol_version >= 461 else \
|
||||||
|
0x39 if context.protocol_version >= 451 else \
|
||||||
|
0x38 if context.protocol_version >= 389 else \
|
||||||
|
0x37 if context.protocol_version >= 352 else \
|
||||||
|
0x36 if context.protocol_version >= 345 else \
|
||||||
|
0x35 if context.protocol_version >= 336 else \
|
||||||
|
0x34 if context.protocol_version >= 332 else \
|
||||||
|
0x35 if context.protocol_version >= 318 else \
|
||||||
|
0x33 if context.protocol_version >= 70 else \
|
||||||
|
0x07
|
||||||
|
|
||||||
|
packet_name = 'respawn'
|
||||||
|
get_definition = staticmethod(lambda context: [
|
||||||
|
{'dimension':
|
||||||
|
NBT if context.protocol_version >= 748 else
|
||||||
|
String if context.protocol_version >= 718 else
|
||||||
|
Integer},
|
||||||
|
{'world_name': String} if context.protocol_version >= 719 else {},
|
||||||
|
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {},
|
||||||
|
{'hashed_seed': Long} if context.protocol_version >= 552 else {},
|
||||||
|
{'game_mode': UnsignedByte},
|
||||||
|
{'previous_game_mode': UnsignedByte}
|
||||||
|
if context.protocol_version >= 730 else {},
|
||||||
|
{'level_type': String} if context.protocol_version < 716 else {},
|
||||||
|
{'is_debug': Boolean} if context.protocol_version >= 716 else {},
|
||||||
|
{'is_flat': Boolean} if context.protocol_version >= 716 else {},
|
||||||
|
{'copy_metadata': Boolean} if context.protocol_version >= 714 else {},
|
||||||
|
])
|
||||||
|
|
||||||
|
# These aliases declare the Enum type corresponding to each field:
|
||||||
|
Difficulty = Difficulty
|
||||||
|
GameMode = GameMode
|
@ -8,7 +8,9 @@ from minecraft.networking.types import (
|
|||||||
class MapPacket(Packet):
|
class MapPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x27 if context.protocol_version >= 550 else \
|
return 0x25 if context.protocol_version >= 741 else \
|
||||||
|
0x26 if context.protocol_version >= 721 else \
|
||||||
|
0x27 if context.protocol_version >= 550 else \
|
||||||
0x26 if context.protocol_version >= 389 else \
|
0x26 if context.protocol_version >= 389 else \
|
||||||
0x25 if context.protocol_version >= 345 else \
|
0x25 if context.protocol_version >= 345 else \
|
||||||
0x24 if context.protocol_version >= 334 else \
|
0x24 if context.protocol_version >= 334 else \
|
||||||
|
@ -9,7 +9,9 @@ from minecraft.networking.types import (
|
|||||||
class PlayerListItemPacket(Packet):
|
class PlayerListItemPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x34 if context.protocol_version >= 550 else \
|
return 0x32 if context.protocol_version >= 741 else \
|
||||||
|
0x33 if context.protocol_version >= 721 else \
|
||||||
|
0x34 if context.protocol_version >= 550 else \
|
||||||
0x33 if context.protocol_version >= 471 else \
|
0x33 if context.protocol_version >= 471 else \
|
||||||
0x31 if context.protocol_version >= 451 else \
|
0x31 if context.protocol_version >= 451 else \
|
||||||
0x30 if context.protocol_version >= 389 else \
|
0x30 if context.protocol_version >= 389 else \
|
||||||
|
@ -9,7 +9,9 @@ from minecraft.networking.types import (
|
|||||||
class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
|
class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x36 if context.protocol_version >= 550 else \
|
return 0x34 if context.protocol_version >= 741 else \
|
||||||
|
0x35 if context.protocol_version >= 721 else \
|
||||||
|
0x36 if context.protocol_version >= 550 else \
|
||||||
0x35 if context.protocol_version >= 471 else \
|
0x35 if context.protocol_version >= 471 else \
|
||||||
0x33 if context.protocol_version >= 451 else \
|
0x33 if context.protocol_version >= 451 else \
|
||||||
0x32 if context.protocol_version >= 389 else \
|
0x32 if context.protocol_version >= 389 else \
|
||||||
|
@ -9,11 +9,11 @@ __all__ = 'SoundEffectPacket',
|
|||||||
class SoundEffectPacket(Packet):
|
class SoundEffectPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x52 if context.protocol_version >= 550 else \
|
return 0x51 if context.protocol_version >= 721 else \
|
||||||
|
0x52 if context.protocol_version >= 550 else \
|
||||||
0x51 if context.protocol_version >= 471 else \
|
0x51 if context.protocol_version >= 471 else \
|
||||||
0x4D if context.protocol_version >= 461 else \
|
0x4D if context.protocol_version >= 461 else \
|
||||||
0x4E if context.protocol_version >= 451 else \
|
0x4E if context.protocol_version >= 451 else \
|
||||||
0x4E if context.protocol_version >= 451 else \
|
|
||||||
0x4D if context.protocol_version >= 389 else \
|
0x4D if context.protocol_version >= 389 else \
|
||||||
0x4C if context.protocol_version >= 352 else \
|
0x4C if context.protocol_version >= 352 else \
|
||||||
0x4B if context.protocol_version >= 345 else \
|
0x4B if context.protocol_version >= 345 else \
|
||||||
|
@ -49,6 +49,7 @@ class SpawnObjectPacket(Packet):
|
|||||||
return getattr(cls, name)
|
return getattr(cls, name)
|
||||||
|
|
||||||
class EntityType(Enum):
|
class EntityType(Enum):
|
||||||
|
# XXX This has not been updated for >= v1.15
|
||||||
ACTIVATED_TNT = 50 if pv < 458 else 55 # PrimedTnt
|
ACTIVATED_TNT = 50 if pv < 458 else 55 # PrimedTnt
|
||||||
AREA_EFFECT_CLOUD = 3 if pv < 458 else 0
|
AREA_EFFECT_CLOUD = 3 if pv < 458 else 0
|
||||||
ARMORSTAND = 78 if pv < 458 else 1
|
ARMORSTAND = 78 if pv < 458 else 1
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
from .packet_buffer import PacketBuffer
|
|
||||||
from zlib import compress
|
from zlib import compress
|
||||||
|
|
||||||
|
from .packet_buffer import PacketBuffer
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Enum
|
VarInt, Enum, overridable_property,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Packet(object):
|
class Packet(object):
|
||||||
packet_name = "base"
|
packet_name = "base"
|
||||||
id = None
|
|
||||||
definition = None
|
|
||||||
|
|
||||||
# To define the packet ID, either:
|
# To define the packet ID, either:
|
||||||
# 1. Define the attribute `id', of type int, in a subclass; or
|
# 1. Define the attribute `id', of type int, in a subclass; or
|
||||||
# 2. Override `get_id' in a subclass and return the correct packet ID
|
# 2. Override `get_id' in a subclass and return the correct packet ID
|
||||||
# for the given ConnectionContext. This is necessary if the packet ID
|
# for the given ConnectionContext. This is necessary if the packet ID
|
||||||
# has changed across protocol versions, for example.
|
# has changed across protocol versions, for example; or
|
||||||
|
# 3. Define the attribute `id' in an instance of a class without either
|
||||||
|
# of the above.
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_id(cls, context):
|
def get_id(cls, _context):
|
||||||
return cls.id
|
return getattr(cls, 'id')
|
||||||
|
|
||||||
|
@overridable_property
|
||||||
|
def id(self):
|
||||||
|
return None if self.context is None else self.get_id(self.context)
|
||||||
|
|
||||||
# To define the network data layout of a packet, either:
|
# To define the network data layout of a packet, either:
|
||||||
# 1. Define the attribute `definition', a list of fields, each of which
|
# 1. Define the attribute `definition', a list of fields, each of which
|
||||||
@ -29,37 +34,37 @@ class Packet(object):
|
|||||||
# This may be necessary if the packet layout cannot be described as a
|
# This may be necessary if the packet layout cannot be described as a
|
||||||
# simple list of fields.
|
# simple list of fields.
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_definition(cls, context):
|
def get_definition(cls, _context):
|
||||||
return cls.definition
|
return getattr(cls, 'definition')
|
||||||
|
|
||||||
|
@overridable_property
|
||||||
|
def definition(self):
|
||||||
|
return None if self.context is None else \
|
||||||
|
self.get_definition(self.context)
|
||||||
|
|
||||||
|
# In general, a packet instance must have its 'context' attribute set to an
|
||||||
|
# instance of 'ConnectionContext', for example to decide on version-
|
||||||
|
# dependent behaviour. This can either be given as an argument to this
|
||||||
|
# constructor (e.g. 'p = P(context=c)') or set later
|
||||||
|
# (e.g. 'p.context = c').
|
||||||
|
#
|
||||||
|
# While a packet has no 'context' set, all attributes should *writable*
|
||||||
|
# without errors, but some attributes may not be *readable*.
|
||||||
|
#
|
||||||
|
# When sending or receiving packets via 'Connection', it is generally not
|
||||||
|
# necessary to set the 'context', as this will be done automatically by
|
||||||
|
# 'Connection'.
|
||||||
def __init__(self, context=None, **kwargs):
|
def __init__(self, context=None, **kwargs):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.set_values(**kwargs)
|
self.set_values(**kwargs)
|
||||||
|
|
||||||
@property
|
|
||||||
def context(self):
|
|
||||||
return self._context
|
|
||||||
|
|
||||||
@context.setter
|
|
||||||
def context(self, _context):
|
|
||||||
self._context = _context
|
|
||||||
self._context_changed()
|
|
||||||
|
|
||||||
def _context_changed(self):
|
|
||||||
if self._context is not None:
|
|
||||||
self.id = self.get_id(self._context)
|
|
||||||
self.definition = self.get_definition(self._context)
|
|
||||||
else:
|
|
||||||
self.id = None
|
|
||||||
self.definition = None
|
|
||||||
|
|
||||||
def set_values(self, **kwargs):
|
def set_values(self, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
for field in self.definition:
|
for field in self.definition: # pylint: disable=not-an-iterable
|
||||||
for var_name, data_type in field.items():
|
for var_name, data_type in field.items():
|
||||||
value = data_type.read_with_context(file_object, self.context)
|
value = data_type.read_with_context(file_object, self.context)
|
||||||
setattr(self, var_name, value)
|
setattr(self, var_name, value)
|
||||||
@ -101,7 +106,7 @@ class Packet(object):
|
|||||||
def write_fields(self, packet_buffer):
|
def write_fields(self, packet_buffer):
|
||||||
# Write the fields comprising the body of the packet (excluding the
|
# Write the fields comprising the body of the packet (excluding the
|
||||||
# length, packet ID, compression and encryption) into a PacketBuffer.
|
# length, packet ID, compression and encryption) into a PacketBuffer.
|
||||||
for field in self.definition:
|
for field in self.definition: # pylint: disable=not-an-iterable
|
||||||
for var_name, data_type in field.items():
|
for var_name, data_type in field.items():
|
||||||
data = getattr(self, var_name)
|
data = getattr(self, var_name)
|
||||||
data_type.send_with_context(data, packet_buffer, self.context)
|
data_type.send_with_context(data, packet_buffer, self.context)
|
||||||
@ -122,6 +127,7 @@ class Packet(object):
|
|||||||
""" An iterable of the names of the packet's fields, or None. """
|
""" An iterable of the names of the packet's fields, or None. """
|
||||||
if self.definition is None:
|
if self.definition is None:
|
||||||
return None
|
return None
|
||||||
|
# pylint: disable=not-an-iterable
|
||||||
return (field for defn in self.definition for field in defn)
|
return (field for defn in self.definition for field in defn)
|
||||||
|
|
||||||
def field_string(self, field):
|
def field_string(self, field):
|
||||||
|
@ -5,7 +5,7 @@ from minecraft.networking.packets import (
|
|||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Double, Float, Boolean, VarInt, String, Byte, Position, Enum,
|
Double, Float, Boolean, VarInt, String, Byte, Position, Enum,
|
||||||
RelativeHand, BlockFace, Vector, Direction, PositionAndLook,
|
RelativeHand, BlockFace, Vector, Direction, PositionAndLook,
|
||||||
multi_attribute_alias
|
multi_attribute_alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .client_settings_packet import ClientSettingsPacket
|
from .client_settings_packet import ClientSettingsPacket
|
||||||
@ -37,7 +37,8 @@ def get_packets(context):
|
|||||||
class KeepAlivePacket(AbstractKeepAlivePacket):
|
class KeepAlivePacket(AbstractKeepAlivePacket):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x0F if context.protocol_version >= 471 else \
|
return 0x10 if context.protocol_version >= 712 else \
|
||||||
|
0x0F if context.protocol_version >= 471 else \
|
||||||
0x10 if context.protocol_version >= 464 else \
|
0x10 if context.protocol_version >= 464 else \
|
||||||
0x0E if context.protocol_version >= 389 else \
|
0x0E if context.protocol_version >= 389 else \
|
||||||
0x0C if context.protocol_version >= 386 else \
|
0x0C if context.protocol_version >= 386 else \
|
||||||
@ -78,7 +79,8 @@ class ChatPacket(Packet):
|
|||||||
class PositionAndLookPacket(Packet):
|
class PositionAndLookPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x12 if context.protocol_version >= 471 else \
|
return 0x13 if context.protocol_version >= 712 else \
|
||||||
|
0x12 if context.protocol_version >= 471 else \
|
||||||
0x13 if context.protocol_version >= 464 else \
|
0x13 if context.protocol_version >= 464 else \
|
||||||
0x11 if context.protocol_version >= 389 else \
|
0x11 if context.protocol_version >= 389 else \
|
||||||
0x0F if context.protocol_version >= 386 else \
|
0x0F if context.protocol_version >= 386 else \
|
||||||
@ -124,7 +126,9 @@ class TeleportConfirmPacket(Packet):
|
|||||||
class AnimationPacket(Packet):
|
class AnimationPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x2A if context.protocol_version >= 468 else \
|
return 0x2C if context.protocol_version >= 738 else \
|
||||||
|
0x2B if context.protocol_version >= 712 else \
|
||||||
|
0x2A if context.protocol_version >= 468 else \
|
||||||
0x29 if context.protocol_version >= 464 else \
|
0x29 if context.protocol_version >= 464 else \
|
||||||
0x27 if context.protocol_version >= 389 else \
|
0x27 if context.protocol_version >= 389 else \
|
||||||
0x25 if context.protocol_version >= 386 else \
|
0x25 if context.protocol_version >= 386 else \
|
||||||
@ -197,7 +201,9 @@ class PlayerBlockPlacementPacket(Packet):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x2C if context.protocol_version >= 468 else \
|
return 0x2E if context.protocol_version >= 738 else \
|
||||||
|
0x2D if context.protocol_version >= 712 else \
|
||||||
|
0x2C if context.protocol_version >= 468 else \
|
||||||
0x2B if context.protocol_version >= 464 else \
|
0x2B if context.protocol_version >= 464 else \
|
||||||
0x29 if context.protocol_version >= 389 else \
|
0x29 if context.protocol_version >= 389 else \
|
||||||
0x27 if context.protocol_version >= 386 else \
|
0x27 if context.protocol_version >= 386 else \
|
||||||
@ -234,7 +240,9 @@ class PlayerBlockPlacementPacket(Packet):
|
|||||||
class UseItemPacket(Packet):
|
class UseItemPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x2D if context.protocol_version >= 468 else \
|
return 0x2F if context.protocol_version >= 738 else \
|
||||||
|
0x2E if context.protocol_version >= 712 else \
|
||||||
|
0x2D if context.protocol_version >= 468 else \
|
||||||
0x2C if context.protocol_version >= 464 else \
|
0x2C if context.protocol_version >= 464 else \
|
||||||
0x2A if context.protocol_version >= 389 else \
|
0x2A if context.protocol_version >= 389 else \
|
||||||
0x28 if context.protocol_version >= 386 else \
|
0x28 if context.protocol_version >= 386 else \
|
||||||
|
@ -2,32 +2,35 @@
|
|||||||
Each type has a method which is used to read and write it.
|
Each type has a method which is used to read and write it.
|
||||||
These definitions and methods are used by the packet definitions
|
These definitions and methods are used by the packet definitions
|
||||||
"""
|
"""
|
||||||
from __future__ import division
|
|
||||||
import struct
|
import struct
|
||||||
import uuid
|
import uuid
|
||||||
|
import io
|
||||||
|
|
||||||
from .utility import Vector
|
import pynbt
|
||||||
|
|
||||||
|
from .utility import Vector, class_and_instancemethod
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Type', 'Boolean', 'UnsignedByte', 'Byte', 'Short', 'UnsignedShort',
|
'Type', 'Boolean', 'UnsignedByte', 'Byte', 'Short', 'UnsignedShort',
|
||||||
'Integer', 'FixedPointInteger', 'Angle', 'VarInt', 'Long',
|
'Integer', 'FixedPointInteger', 'Angle', 'VarInt', 'VarLong', 'Long',
|
||||||
'UnsignedLong', 'Float', 'Double', 'ShortPrefixedByteArray',
|
'UnsignedLong', 'Float', 'Double', 'ShortPrefixedByteArray',
|
||||||
'VarIntPrefixedByteArray', 'TrailingByteArray', 'String', 'UUID',
|
'VarIntPrefixedByteArray', 'TrailingByteArray', 'String', 'UUID',
|
||||||
'Position',
|
'Position', 'NBT', 'PrefixedArray',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Type(object):
|
class Type(object):
|
||||||
|
# pylint: disable=no-self-argument
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@classmethod
|
@class_and_instancemethod
|
||||||
def read_with_context(cls, file_object, _context):
|
def read_with_context(cls_or_self, file_object, _context):
|
||||||
return cls.read(file_object)
|
return cls_or_self.read(file_object)
|
||||||
|
|
||||||
@classmethod
|
@class_and_instancemethod
|
||||||
def send_with_context(cls, value, socket, _context):
|
def send_with_context(cls_or_self, value, socket, _context):
|
||||||
return cls.send(value, socket)
|
return cls_or_self.send(value, socket)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def read(cls, file_object):
|
def read(cls, file_object):
|
||||||
@ -131,12 +134,13 @@ class Angle(Type):
|
|||||||
|
|
||||||
|
|
||||||
class VarInt(Type):
|
class VarInt(Type):
|
||||||
@staticmethod
|
max_bytes = 5
|
||||||
def read(file_object):
|
|
||||||
|
@classmethod
|
||||||
|
def read(cls, file_object):
|
||||||
number = 0
|
number = 0
|
||||||
# Limit of 5 bytes, otherwise its possible to cause
|
# Limit of 'cls.max_bytes' bytes, otherwise its possible to cause
|
||||||
# a DOS attack by sending VarInts that just keep
|
# a DOS attack by sending VarInts that just keep going
|
||||||
# going
|
|
||||||
bytes_encountered = 0
|
bytes_encountered = 0
|
||||||
while True:
|
while True:
|
||||||
byte = file_object.read(1)
|
byte = file_object.read(1)
|
||||||
@ -149,7 +153,7 @@ class VarInt(Type):
|
|||||||
break
|
break
|
||||||
|
|
||||||
bytes_encountered += 1
|
bytes_encountered += 1
|
||||||
if bytes_encountered > 5:
|
if bytes_encountered > cls.max_bytes:
|
||||||
raise ValueError("Tried to read too long of a VarInt")
|
raise ValueError("Tried to read too long of a VarInt")
|
||||||
return number
|
return number
|
||||||
|
|
||||||
@ -172,6 +176,10 @@ class VarInt(Type):
|
|||||||
raise ValueError("Integer too large")
|
raise ValueError("Integer too large")
|
||||||
|
|
||||||
|
|
||||||
|
class VarLong(VarInt):
|
||||||
|
max_bytes = 10
|
||||||
|
|
||||||
|
|
||||||
# Maps (maximum integer value -> size of VarInt in bytes)
|
# Maps (maximum integer value -> size of VarInt in bytes)
|
||||||
VARINT_SIZE_TABLE = {
|
VARINT_SIZE_TABLE = {
|
||||||
2 ** 7: 1,
|
2 ** 7: 1,
|
||||||
@ -324,3 +332,48 @@ class Position(Type, Vector):
|
|||||||
if context.protocol_version >= 443 else
|
if context.protocol_version >= 443 else
|
||||||
(x & 0x3FFFFFF) << 38 | (y & 0xFFF) << 26 | (z & 0x3FFFFFF))
|
(x & 0x3FFFFFF) << 38 | (y & 0xFFF) << 26 | (z & 0x3FFFFFF))
|
||||||
UnsignedLong.send(value, socket)
|
UnsignedLong.send(value, socket)
|
||||||
|
|
||||||
|
|
||||||
|
class NBT(Type):
|
||||||
|
@staticmethod
|
||||||
|
def read(file_object):
|
||||||
|
return pynbt.NBTFile(io=file_object)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send(value, socket):
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
pynbt.NBTFile(value=value).save(buffer)
|
||||||
|
socket.send(buffer.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
class PrefixedArray(Type):
|
||||||
|
__slots__ = 'length_type', 'element_type'
|
||||||
|
|
||||||
|
def __init__(self, length_type, element_type):
|
||||||
|
self.length_type = length_type
|
||||||
|
self.element_type = element_type
|
||||||
|
|
||||||
|
def read(self, file_object):
|
||||||
|
return self.__read(file_object, self.element_type.read)
|
||||||
|
|
||||||
|
def send(self, value, socket):
|
||||||
|
return self.__send(value, socket, self.element_type.send)
|
||||||
|
|
||||||
|
def read_with_context(self, file_object, context):
|
||||||
|
def element_read(file_object):
|
||||||
|
return self.element_type.read_with_context(file_object, context)
|
||||||
|
return self.__read(file_object, element_read)
|
||||||
|
|
||||||
|
def send_with_context(self, value, socket, context):
|
||||||
|
def element_send(value, socket):
|
||||||
|
return self.element_type.send_with_context(value, socket, context)
|
||||||
|
return self.__send(value, socket, element_send)
|
||||||
|
|
||||||
|
def __read(self, file_object, element_read):
|
||||||
|
length = self.length_type.read(file_object)
|
||||||
|
return [element_read(file_object) for i in range(length)]
|
||||||
|
|
||||||
|
def __send(self, value, socket, element_send):
|
||||||
|
self.length_type.send(len(value), socket)
|
||||||
|
for element in value:
|
||||||
|
element_send(element, socket)
|
||||||
|
@ -99,13 +99,22 @@ class Dimension(Enum):
|
|||||||
OVERWORLD = 0
|
OVERWORLD = 0
|
||||||
END = 1
|
END = 1
|
||||||
|
|
||||||
|
from_identifier_dict = {
|
||||||
|
'minecraft:the_nether': NETHER,
|
||||||
|
'minecraft:overworld': OVERWORLD,
|
||||||
|
'minecraft:the_end': END,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_identifier_dict = {e: i for (i, e) in from_identifier_dict.items()}
|
||||||
|
|
||||||
|
|
||||||
# Designation of a player's gamemode.
|
# Designation of a player's gamemode.
|
||||||
class GameMode(Enum):
|
class GameMode(BitFieldEnum):
|
||||||
SURVIVAL = 0
|
SURVIVAL = 0
|
||||||
CREATIVE = 1
|
CREATIVE = 1
|
||||||
ADVENTURE = 2
|
ADVENTURE = 2
|
||||||
SPECTATOR = 3
|
SPECTATOR = 3
|
||||||
|
HARDCORE = 8 # Only used prior to protocol 738.
|
||||||
|
|
||||||
|
|
||||||
# Currently designates an entity's feet or eyes.
|
# Currently designates an entity's feet or eyes.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Minecraft data types that are used by packets, but don't have a specific
|
"""Minecraft data types that are used by packets, but don't have a specific
|
||||||
network representation.
|
network representation.
|
||||||
"""
|
"""
|
||||||
from __future__ import division
|
import types
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
@ -9,7 +9,8 @@ from itertools import chain
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor',
|
'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor',
|
||||||
'attribute_alias', 'multi_attribute_alias',
|
'overridable_descriptor', 'overridable_property', 'attribute_alias',
|
||||||
|
'multi_attribute_alias',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -144,23 +145,58 @@ def multi_attribute_alias(container, *arg_names, **kwd_names):
|
|||||||
return alias
|
return alias
|
||||||
|
|
||||||
|
|
||||||
class descriptor(object):
|
class overridable_descriptor:
|
||||||
"""Behaves identically to the builtin 'property' function of Python,
|
"""As 'descriptor' (defined below), except that only a getter can be
|
||||||
except that the getter, setter and deleter functions given by the
|
defined, and the resulting descriptor has no '__set__' or '__delete__'
|
||||||
user are used as the raw __get__, __set__ and __delete__ functions
|
methods defined; hence, attributes defined via this class can be
|
||||||
as defined in Python's descriptor protocol.
|
overridden by attributes of instances of the class in which it occurs.
|
||||||
"""
|
"""
|
||||||
__slots__ = '_fget', '_fset', '_fdel'
|
__slots__ = '_fget',
|
||||||
|
|
||||||
def __init__(self, fget=None, fset=None, fdel=None):
|
def __init__(self, fget=None):
|
||||||
self._fget = fget if fget is not None else self._default_get
|
self._fget = fget if fget is not None else self._default_get
|
||||||
self._fset = fset if fset is not None else self._default_set
|
|
||||||
self._fdel = fdel if fdel is not None else self._default_del
|
|
||||||
|
|
||||||
def getter(self, fget):
|
def getter(self, fget):
|
||||||
self._fget = fget
|
self._fget = fget
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _default_get(instance, owner):
|
||||||
|
raise AttributeError('unreadable attribute')
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
return self._fget(self, instance, owner)
|
||||||
|
|
||||||
|
|
||||||
|
class overridable_property(overridable_descriptor):
|
||||||
|
"""As the builtin 'property' decorator of Python, except that only
|
||||||
|
a getter is defined and the resulting descriptor is a non-data
|
||||||
|
descriptor, overridable by attributes of instances of the class
|
||||||
|
in which the property occurs. See also 'overridable_descriptor' above.
|
||||||
|
"""
|
||||||
|
def __get__(self, instance, _owner):
|
||||||
|
return self._fget(instance)
|
||||||
|
|
||||||
|
|
||||||
|
class descriptor(overridable_descriptor):
|
||||||
|
"""Behaves identically to the builtin 'property' decorator of Python,
|
||||||
|
except that the getter, setter and deleter functions given by the
|
||||||
|
user are used as the raw __get__, __set__ and __delete__ functions
|
||||||
|
as defined in Python's descriptor protocol.
|
||||||
|
|
||||||
|
Since an instance of this class always havs '__set__' and '__delete__'
|
||||||
|
defined, it is a "data descriptor", so its binding behaviour cannot be
|
||||||
|
overridden in instances of the class in which it occurs. See
|
||||||
|
https://docs.python.org/3/reference/datamodel.html#descriptor-invocation
|
||||||
|
for more information. See also 'overridable_descriptor' above.
|
||||||
|
"""
|
||||||
|
__slots__ = '_fset', '_fdel'
|
||||||
|
|
||||||
|
def __init__(self, fget=None, fset=None, fdel=None):
|
||||||
|
super(descriptor, self).__init__(fget=fget)
|
||||||
|
self._fset = fset if fset is not None else self._default_set
|
||||||
|
self._fdel = fdel if fdel is not None else self._default_del
|
||||||
|
|
||||||
def setter(self, fset):
|
def setter(self, fset):
|
||||||
self._fset = fset
|
self._fset = fset
|
||||||
return self
|
return self
|
||||||
@ -169,10 +205,6 @@ class descriptor(object):
|
|||||||
self._fdel = fdel
|
self._fdel = fdel
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _default_get(instance, owner):
|
|
||||||
raise AttributeError('unreadable attribute')
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _default_set(instance, value):
|
def _default_set(instance, value):
|
||||||
raise AttributeError("can't set attribute")
|
raise AttributeError("can't set attribute")
|
||||||
@ -181,9 +213,6 @@ class descriptor(object):
|
|||||||
def _default_del(instance):
|
def _default_del(instance):
|
||||||
raise AttributeError("can't delete attribute")
|
raise AttributeError("can't delete attribute")
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
|
||||||
return self._fget(self, instance, owner)
|
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
return self._fset(self, instance, value)
|
return self._fset(self, instance, value)
|
||||||
|
|
||||||
@ -191,6 +220,26 @@ class descriptor(object):
|
|||||||
return self._fdel(self, instance)
|
return self._fdel(self, instance)
|
||||||
|
|
||||||
|
|
||||||
|
class class_and_instancemethod:
|
||||||
|
""" A decorator for functions defined in a class namespace which are to be
|
||||||
|
accessed as both class and instance methods: retrieving the method from
|
||||||
|
a class will return a bound class method (like the built-in
|
||||||
|
'classmethod' decorator), but retrieving the method from an instance
|
||||||
|
will return a bound instance method (as if the function were not
|
||||||
|
decorated). Therefore, the first argument of the decorated function may
|
||||||
|
be either a class or an instance, depending on how it was called.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = '_func',
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
self._func = func
|
||||||
|
|
||||||
|
def __get__(self, inst, owner=None):
|
||||||
|
bind_to = owner if inst is None else inst
|
||||||
|
return types.MethodType(self._func, bind_to)
|
||||||
|
|
||||||
|
|
||||||
Direction = namedtuple('Direction', ('yaw', 'pitch'))
|
Direction = namedtuple('Direction', ('yaw', 'pitch'))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
# Package dependencies are stored in setup.py.
|
||||||
|
# For more information, see <https://github.com/ammaraskar/pyCraft/pull/156>.
|
||||||
-e .
|
-e .
|
||||||
|
2
setup.py
2
setup.py
@ -24,7 +24,7 @@ setup(name="pyCraft",
|
|||||||
author=", ".join(MAIN_AUTHORS),
|
author=", ".join(MAIN_AUTHORS),
|
||||||
install_requires=["cryptography>=1.5",
|
install_requires=["cryptography>=1.5",
|
||||||
"requests",
|
"requests",
|
||||||
"future",
|
"pynbt",
|
||||||
],
|
],
|
||||||
packages=["minecraft",
|
packages=["minecraft",
|
||||||
"minecraft.networking",
|
"minecraft.networking",
|
||||||
|
14
start.py
14
start.py
@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import getpass
|
import getpass
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
@ -11,7 +9,6 @@ from minecraft import authentication
|
|||||||
from minecraft.exceptions import YggdrasilError
|
from minecraft.exceptions import YggdrasilError
|
||||||
from minecraft.networking.connection import Connection
|
from minecraft.networking.connection import Connection
|
||||||
from minecraft.networking.packets import Packet, clientbound, serverbound
|
from minecraft.networking.packets import Packet, clientbound, serverbound
|
||||||
from minecraft.compat import input
|
|
||||||
|
|
||||||
|
|
||||||
def get_options():
|
def get_options():
|
||||||
@ -35,6 +32,10 @@ def get_options():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="print sent and received packets to standard error")
|
help="print sent and received packets to standard error")
|
||||||
|
|
||||||
|
parser.add_option("-v", "--dump-unknown-packets", dest="dump_unknown",
|
||||||
|
action="store_true",
|
||||||
|
help="include unknown packets in --dump-packets output")
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
if not options.username:
|
if not options.username:
|
||||||
@ -81,8 +82,11 @@ def main():
|
|||||||
def print_incoming(packet):
|
def print_incoming(packet):
|
||||||
if type(packet) is Packet:
|
if type(packet) is Packet:
|
||||||
# This is a direct instance of the base Packet type, meaning
|
# This is a direct instance of the base Packet type, meaning
|
||||||
# that it is a packet of unknown type, so we do not print it.
|
# that it is a packet of unknown type, so we do not print it
|
||||||
return
|
# unless explicitly requested by the user.
|
||||||
|
if options.dump_unknown:
|
||||||
|
print('--> [unknown packet] %s' % packet, file=sys.stderr)
|
||||||
|
else:
|
||||||
print('--> %s' % packet, file=sys.stderr)
|
print('--> %s' % packet, file=sys.stderr)
|
||||||
|
|
||||||
def print_outgoing(packet):
|
def print_outgoing(packet):
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import platform
|
|
||||||
from distutils.version import StrictVersion
|
|
||||||
|
|
||||||
if StrictVersion(platform.python_version()) < StrictVersion("3.3.0"):
|
|
||||||
import mock # noqa
|
|
||||||
else:
|
|
||||||
from unittest import mock # noqa
|
|
@ -1,4 +1,4 @@
|
|||||||
from __future__ import print_function
|
import pynbt
|
||||||
|
|
||||||
from minecraft import SUPPORTED_MINECRAFT_VERSIONS
|
from minecraft import SUPPORTED_MINECRAFT_VERSIONS
|
||||||
from minecraft.networking import connection
|
from minecraft.networking import connection
|
||||||
@ -11,7 +11,6 @@ from minecraft.networking.encryption import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
|
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
|
||||||
from future.utils import raise_
|
|
||||||
|
|
||||||
from numbers import Integral
|
from numbers import Integral
|
||||||
import unittest
|
import unittest
|
||||||
@ -101,10 +100,46 @@ class FakeClientHandler(object):
|
|||||||
|
|
||||||
def handle_play_start(self):
|
def handle_play_start(self):
|
||||||
# Called upon entering the play state.
|
# Called upon entering the play state.
|
||||||
self.write_packet(clientbound.play.JoinGamePacket(
|
packet = clientbound.play.JoinGamePacket(
|
||||||
entity_id=0, game_mode=0, dimension=0, hashed_seed=12345,
|
entity_id=0, is_hardcore=False, game_mode=0, previous_game_mode=0,
|
||||||
difficulty=2, max_players=1, level_type='default',
|
world_names=['minecraft:overworld'],
|
||||||
reduced_debug_info=False, render_distance=9, respawn_screen=False))
|
world_name='minecraft:overworld',
|
||||||
|
hashed_seed=12345, difficulty=2, max_players=1,
|
||||||
|
level_type='default', reduced_debug_info=False, render_distance=9,
|
||||||
|
respawn_screen=False, is_debug=False, is_flat=False)
|
||||||
|
|
||||||
|
if self.server.context.protocol_version >= 748:
|
||||||
|
packet.dimension = pynbt.TAG_Compound({
|
||||||
|
'natural': pynbt.TAG_Byte(1),
|
||||||
|
'effects': pynbt.TAG_String('minecraft:overworld'),
|
||||||
|
}, '')
|
||||||
|
packet.dimension_codec = pynbt.TAG_Compound({
|
||||||
|
'minecraft:dimension_type': pynbt.TAG_Compound({
|
||||||
|
'type': pynbt.TAG_String('minecraft:dimension_type'),
|
||||||
|
'value': pynbt.TAG_List(pynbt.TAG_Compound, [
|
||||||
|
pynbt.TAG_Compound(packet.dimension),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'minecraft:worldgen/biome': pynbt.TAG_Compound({
|
||||||
|
'type': pynbt.TAG_String('minecraft:worldgen/biome'),
|
||||||
|
'value': pynbt.TAG_List(pynbt.TAG_Compound, [
|
||||||
|
pynbt.TAG_Compound({
|
||||||
|
'id': pynbt.TAG_Int(1),
|
||||||
|
'name': pynbt.TAG_String('minecraft:plains'),
|
||||||
|
}),
|
||||||
|
pynbt.TAG_Compound({
|
||||||
|
'id': pynbt.TAG_Int(2),
|
||||||
|
'name': pynbt.TAG_String('minecraft:desert'),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
}, '')
|
||||||
|
elif self.server.context.protocol_version >= 718:
|
||||||
|
packet.dimension = 'minecraft:overworld'
|
||||||
|
else:
|
||||||
|
packet.dimension = types.Dimension.OVERWORLD
|
||||||
|
|
||||||
|
self.write_packet(packet)
|
||||||
|
|
||||||
def handle_play_packet(self, packet):
|
def handle_play_packet(self, packet):
|
||||||
# Called upon each packet received after handle_play_start() returns.
|
# Called upon each packet received after handle_play_start() returns.
|
||||||
@ -181,7 +216,8 @@ class FakeClientHandler(object):
|
|||||||
try:
|
try:
|
||||||
self.handle_connection()
|
self.handle_connection()
|
||||||
packet = self.read_packet()
|
packet = self.read_packet()
|
||||||
assert isinstance(packet, serverbound.handshake.HandShakePacket)
|
assert isinstance(packet, serverbound.handshake.HandShakePacket), \
|
||||||
|
type(packet)
|
||||||
self.handle_handshake(packet)
|
self.handle_handshake(packet)
|
||||||
if packet.next_state == 1:
|
if packet.next_state == 1:
|
||||||
self._run_status()
|
self._run_status()
|
||||||
@ -576,7 +612,8 @@ class _FakeServerTest(unittest.TestCase):
|
|||||||
logging.error(**error)
|
logging.error(**error)
|
||||||
self.fail('Multiple errors: see logging output.')
|
self.fail('Multiple errors: see logging output.')
|
||||||
elif errors and 'exc_info' in errors[0]:
|
elif errors and 'exc_info' in errors[0]:
|
||||||
raise_(*errors[0]['exc_info'])
|
exc_value, exc_tb = errors[0]['exc_info'][1:]
|
||||||
|
raise exc_value.with_traceback(exc_tb)
|
||||||
elif errors:
|
elif errors:
|
||||||
self.fail(errors[0]['msg'])
|
self.fail(errors[0]['msg'])
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ from minecraft.authentication import _make_request
|
|||||||
from minecraft.authentication import _raise_from_response
|
from minecraft.authentication import _raise_from_response
|
||||||
from minecraft.exceptions import YggdrasilError
|
from minecraft.exceptions import YggdrasilError
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
import unittest
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
import os
|
import os
|
||||||
from .compat import mock
|
|
||||||
|
|
||||||
FAKE_DATA = {
|
FAKE_DATA = {
|
||||||
"id_": "85e2c12b9eab4a7dabf61babc11354c2",
|
"id_": "85e2c12b9eab4a7dabf61babc11354c2",
|
||||||
|
@ -101,3 +101,56 @@ class ClassMemberAliasesTest(unittest.TestCase):
|
|||||||
packet = clientbound.play.BlockChangePacket(blockId=bi, blockMeta=bm)
|
packet = clientbound.play.BlockChangePacket(blockId=bi, blockMeta=bm)
|
||||||
self.assertEqual((packet.blockId, packet.blockMeta), (bi, bm))
|
self.assertEqual((packet.blockId, packet.blockMeta), (bi, bm))
|
||||||
self.assertEqual(packet.blockStateId, packet.block_state_id)
|
self.assertEqual(packet.blockStateId, packet.block_state_id)
|
||||||
|
|
||||||
|
def test_join_game_packet(self):
|
||||||
|
GameMode = types.GameMode
|
||||||
|
context = ConnectionContext()
|
||||||
|
for pure_game_mode in (GameMode.SURVIVAL, GameMode.CREATIVE,
|
||||||
|
GameMode.ADVENTURE, GameMode.SPECTATOR):
|
||||||
|
for is_hardcore in (False, True):
|
||||||
|
context.protocol_version = 70
|
||||||
|
game_mode = \
|
||||||
|
pure_game_mode | GameMode.HARDCORE \
|
||||||
|
if is_hardcore else pure_game_mode
|
||||||
|
|
||||||
|
packet = clientbound.play.JoinGamePacket()
|
||||||
|
packet.game_mode = game_mode
|
||||||
|
packet.context = context
|
||||||
|
self.assertEqual(packet.pure_game_mode, pure_game_mode)
|
||||||
|
self.assertEqual(packet.is_hardcore, is_hardcore)
|
||||||
|
|
||||||
|
del packet.context
|
||||||
|
del packet.is_hardcore
|
||||||
|
packet.context = context
|
||||||
|
self.assertEqual(packet.game_mode, packet.pure_game_mode)
|
||||||
|
|
||||||
|
del packet.context
|
||||||
|
del packet.game_mode
|
||||||
|
packet.context = context
|
||||||
|
self.assertFalse(hasattr(packet, 'is_hardcore'))
|
||||||
|
|
||||||
|
packet = clientbound.play.JoinGamePacket()
|
||||||
|
packet.pure_game_mode = game_mode
|
||||||
|
packet.is_hardcore = is_hardcore
|
||||||
|
packet.context = context
|
||||||
|
self.assertEqual(packet.game_mode, game_mode)
|
||||||
|
|
||||||
|
context.protocol_version = 738
|
||||||
|
game_mode = pure_game_mode | GameMode.HARDCORE
|
||||||
|
|
||||||
|
packet = clientbound.play.JoinGamePacket()
|
||||||
|
packet.game_mode = game_mode
|
||||||
|
packet.is_hardcore = is_hardcore
|
||||||
|
packet.context = context
|
||||||
|
self.assertEqual(packet.game_mode, game_mode)
|
||||||
|
self.assertEqual(packet.pure_game_mode, game_mode)
|
||||||
|
self.assertEqual(packet.is_hardcore, is_hardcore)
|
||||||
|
|
||||||
|
del packet.context
|
||||||
|
packet.is_hardcore = is_hardcore
|
||||||
|
packet.context = context
|
||||||
|
self.assertEqual(packet.game_mode, game_mode)
|
||||||
|
self.assertEqual(packet.pure_game_mode, game_mode)
|
||||||
|
|
||||||
|
with self.assertRaises(AttributeError):
|
||||||
|
del packet.pure_game_mode
|
||||||
|
@ -5,7 +5,6 @@ from minecraft.networking.connection import Connection
|
|||||||
from minecraft.exceptions import (
|
from minecraft.exceptions import (
|
||||||
VersionMismatch, LoginDisconnect, InvalidState, IgnorePacket
|
VersionMismatch, LoginDisconnect, InvalidState, IgnorePacket
|
||||||
)
|
)
|
||||||
from minecraft.compat import unicode
|
|
||||||
|
|
||||||
from . import fake_server
|
from . import fake_server
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ class DefaultStatusTest(ConnectTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
class FakeStdOut(io.BytesIO):
|
class FakeStdOut(io.BytesIO):
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
if isinstance(data, unicode):
|
if isinstance(data, str):
|
||||||
data = data.encode('utf8')
|
data = data.encode('utf8')
|
||||||
super(FakeStdOut, self).write(data)
|
super(FakeStdOut, self).write(data)
|
||||||
sys.stdout, self.old_stdout = FakeStdOut(), sys.stdout
|
sys.stdout, self.old_stdout = FakeStdOut(), sys.stdout
|
||||||
|
@ -186,11 +186,9 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
packet = clientbound.play.CombatEventPacket(
|
packet = clientbound.play.CombatEventPacket(
|
||||||
event=clientbound.play.CombatEventPacket.EndCombatEvent(
|
event=clientbound.play.CombatEventPacket.EndCombatEvent(
|
||||||
duration=415, entity_id=91063502))
|
duration=415, entity_id=91063502))
|
||||||
self.assertEqual(
|
self.assertEqual(str(packet),
|
||||||
str(packet),
|
|
||||||
'CombatEventPacket(event=EndCombatEvent('
|
'CombatEventPacket(event=EndCombatEvent('
|
||||||
'duration=415, entity_id=91063502))'
|
'duration=415, entity_id=91063502))')
|
||||||
)
|
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
packet = clientbound.play.CombatEventPacket(
|
packet = clientbound.play.CombatEventPacket(
|
||||||
@ -205,26 +203,32 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
|
|
||||||
def test_multi_block_change_packet(self):
|
def test_multi_block_change_packet(self):
|
||||||
Record = clientbound.play.MultiBlockChangePacket.Record
|
Record = clientbound.play.MultiBlockChangePacket.Record
|
||||||
packet = clientbound.play.MultiBlockChangePacket(
|
|
||||||
chunk_x=167, chunk_z=15, records=[
|
for protocol_version in TEST_VERSIONS:
|
||||||
|
context = ConnectionContext()
|
||||||
|
context.protocol_version = protocol_version
|
||||||
|
packet = clientbound.play.MultiBlockChangePacket(context)
|
||||||
|
|
||||||
|
if protocol_version >= 741:
|
||||||
|
packet.chunk_section_pos = Vector(167, 17, 33)
|
||||||
|
packet.invert_trust_edges = False
|
||||||
|
else:
|
||||||
|
packet.chunk_x, packet.chunk_z = 167, 17
|
||||||
|
self.assertEqual(packet.chunk_pos, (167, 17))
|
||||||
|
|
||||||
|
packet.records = [
|
||||||
Record(x=1, y=2, z=3, blockId=56, blockMeta=13),
|
Record(x=1, y=2, z=3, blockId=56, blockMeta=13),
|
||||||
Record(position=Vector(1, 2, 3), block_state_id=909),
|
Record(position=Vector(1, 2, 3), block_state_id=909),
|
||||||
Record(position=(1, 2, 3), blockStateId=909)])
|
Record(position=(1, 2, 3), blockStateId=909),
|
||||||
self.assertEqual(packet.records[0].blockId, 56)
|
]
|
||||||
self.assertEqual(packet.records[0].blockMeta, 13)
|
|
||||||
self.assertEqual(packet.records[0].blockStateId, 909)
|
|
||||||
self.assertEqual(packet.records[0].position, Vector(1, 2, 3))
|
|
||||||
self.assertEqual(packet.chunk_pos, (packet.chunk_x, packet.chunk_z))
|
|
||||||
|
|
||||||
self.assertEqual(
|
for i in range(3):
|
||||||
str(packet),
|
self.assertEqual(packet.records[i].blockId, 56)
|
||||||
'MultiBlockChangePacket(chunk_x=167, chunk_z=15, records=['
|
self.assertEqual(packet.records[i].blockMeta, 13)
|
||||||
'Record(x=1, y=2, z=3, block_state_id=909), '
|
self.assertEqual(packet.records[i].blockStateId, 909)
|
||||||
'Record(x=1, y=2, z=3, block_state_id=909), '
|
self.assertEqual(packet.records[i].position, Vector(1, 2, 3))
|
||||||
'Record(x=1, y=2, z=3, block_state_id=909)])'
|
|
||||||
)
|
|
||||||
|
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet, context)
|
||||||
|
|
||||||
def test_spawn_object_packet(self):
|
def test_spawn_object_packet(self):
|
||||||
for protocol_version in TEST_VERSIONS:
|
for protocol_version in TEST_VERSIONS:
|
||||||
|
7
tox.ini
7
tox.ini
@ -4,7 +4,7 @@
|
|||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py27, py35, py36, py37, py38, pypy, flake8, pylint-errors, pylint-full, verify-manifest
|
envlist = py35, py36, py37, py38, pypy, flake8, pylint-errors, pylint-full, verify-manifest
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands = nosetests --with-timer
|
commands = nosetests --with-timer
|
||||||
@ -27,11 +27,6 @@ commands =
|
|||||||
deps =
|
deps =
|
||||||
coveralls
|
coveralls
|
||||||
|
|
||||||
[testenv:py27]
|
|
||||||
deps =
|
|
||||||
{[testenv]deps}
|
|
||||||
mock
|
|
||||||
|
|
||||||
[testenv:py38]
|
[testenv:py38]
|
||||||
setenv =
|
setenv =
|
||||||
PYCRAFT_RUN_INTERNET_TESTS=1
|
PYCRAFT_RUN_INTERNET_TESTS=1
|
||||||
|
Loading…
Reference in New Issue
Block a user