mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2025-02-18 04:41:27 +01:00
Improve metadata and auxiliary methods of existing packets.
* Add multi-attribute aliases to some packets, for user convenience. * Add support for writing PlayerListItemPacket. * Add 'fields' attributes to manually-read/written packet classes, implementing 'field_string' where appropriate to allow enable the default __repr__ implementation. * Modify data constructors where appropriate so that __repr__ implementations match their constructor protocols. * Improve comments on type aliases within packet classes. * Add/modify tests to cover the new functionality.
This commit is contained in:
parent
234e57716c
commit
e3d2b1a368
@ -4,7 +4,8 @@ from minecraft.networking.packets import (
|
|||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Integer, FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID,
|
Integer, FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID,
|
||||||
Short, VarInt, Double, Float, String, Enum, Difficulty, Dimension, GameMode
|
Short, VarInt, Double, Float, String, Enum, Difficulty, Dimension,
|
||||||
|
GameMode, Vector, Direction, PositionAndLook, multi_attribute_alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .combat_event_packet import CombatEventPacket
|
from .combat_event_packet import CombatEventPacket
|
||||||
@ -91,13 +92,9 @@ class JoinGamePacket(Packet):
|
|||||||
{'reduced_debug_info': Boolean},
|
{'reduced_debug_info': Boolean},
|
||||||
])
|
])
|
||||||
|
|
||||||
# JoinGamePacket.Difficulty is an alias for Difficulty
|
# These aliases declare the Enum type corresponding to each field:
|
||||||
Difficulty = Difficulty
|
Difficulty = Difficulty
|
||||||
|
|
||||||
# JoinGamePacket.Gamemode is an alias for Gamemode
|
|
||||||
GameMode = GameMode
|
GameMode = GameMode
|
||||||
|
|
||||||
# JoinGamePacket.Dimension is an alias for Dimension
|
|
||||||
Dimension = Dimension
|
Dimension = Dimension
|
||||||
|
|
||||||
|
|
||||||
@ -115,7 +112,7 @@ class ServerDifficultyPacket(Packet):
|
|||||||
{'is_locked': Boolean} if context.protocol_version >= 464 else {},
|
{'is_locked': Boolean} if context.protocol_version >= 464 else {},
|
||||||
])
|
])
|
||||||
|
|
||||||
# ServerDifficultyPacket.Difficulty is an alias for Difficulty
|
# These aliases declare the Enum type corresponding to each field:
|
||||||
Difficulty = Difficulty
|
Difficulty = Difficulty
|
||||||
|
|
||||||
|
|
||||||
@ -181,10 +178,22 @@ class SpawnPlayerPacket(Packet):
|
|||||||
else {'z': FixedPointInteger},
|
else {'z': FixedPointInteger},
|
||||||
{'yaw': Angle},
|
{'yaw': Angle},
|
||||||
{'pitch': Angle},
|
{'pitch': Angle},
|
||||||
|
{'current_item': Short} if context.protocol_version <= 49 else {},
|
||||||
# TODO: read entity metadata
|
# TODO: read entity metadata
|
||||||
{'current_item': Short} if context.protocol_version <= 49 else {}
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
||||||
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
|
|
||||||
|
# Access the 'yaw', 'pitch' fields as a Direction tuple.
|
||||||
|
look = multi_attribute_alias(Direction, 'yaw', 'pitch')
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z', 'yaw', 'pitch' fields as a PositionAndLook.
|
||||||
|
# NOTE: modifying the object retrieved from this property will not change
|
||||||
|
# the packet; it can only be changed by attribute or property assignment.
|
||||||
|
position_and_look = multi_attribute_alias(
|
||||||
|
PositionAndLook, 'x', 'y', 'z', 'yaw', 'pitch')
|
||||||
|
|
||||||
|
|
||||||
class EntityVelocityPacket(Packet):
|
class EntityVelocityPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -258,13 +267,9 @@ class RespawnPacket(Packet):
|
|||||||
{'level_type': String},
|
{'level_type': String},
|
||||||
])
|
])
|
||||||
|
|
||||||
# RespawnPacket.Difficulty is an alias for Difficulty.
|
# These aliases declare the Enum type corresponding to each field:
|
||||||
Difficulty = Difficulty
|
Difficulty = Difficulty
|
||||||
|
|
||||||
# RespawnPacket.Dimension is an alias for Dimension.
|
|
||||||
Dimension = Dimension
|
Dimension = Dimension
|
||||||
|
|
||||||
# RespawnPacket.Gamemode is an alias for Gamemode.
|
|
||||||
GameMode = GameMode
|
GameMode = GameMode
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +53,11 @@ class MultiBlockChangePacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'multi block change'
|
packet_name = 'multi block change'
|
||||||
|
|
||||||
|
fields = 'chunk_x', 'chunk_z', 'records'
|
||||||
|
|
||||||
|
# Access the 'chunk_x' and 'chunk_z' fields as a tuple.
|
||||||
|
chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z')
|
||||||
|
|
||||||
class Record(MutableRecord):
|
class Record(MutableRecord):
|
||||||
__slots__ = 'x', 'y', 'z', 'block_state_id'
|
__slots__ = 'x', 'y', 'z', 'block_state_id'
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ class CombatEventPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'combat event'
|
packet_name = 'combat event'
|
||||||
|
|
||||||
|
fields = 'event',
|
||||||
|
|
||||||
# The abstract type of the 'event' field of this packet.
|
# The abstract type of the 'event' field of this packet.
|
||||||
class EventType(MutableRecord):
|
class EventType(MutableRecord):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
@ -18,14 +18,19 @@ class ExplosionPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'explosion'
|
packet_name = 'explosion'
|
||||||
|
|
||||||
class Record(Vector):
|
fields = 'x', 'y', 'z', 'radius', 'records', \
|
||||||
__slots__ = ()
|
'player_motion_x', 'player_motion_y', 'player_motion_z'
|
||||||
|
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
# 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):
|
def read(self, file_object):
|
||||||
self.x = Float.read(file_object)
|
self.x = Float.read(file_object)
|
||||||
self.y = Float.read(file_object)
|
self.y = Float.read(file_object)
|
||||||
|
@ -15,7 +15,13 @@ class FacePlayerPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'face player'
|
packet_name = 'face player'
|
||||||
|
|
||||||
# Access the 'x', 'y', 'z' fields as a Vector.
|
@property
|
||||||
|
def fields(self):
|
||||||
|
return ('origin', 'x', 'y', 'z', 'entity_id', 'entity_origin') \
|
||||||
|
if self.context.protocol_version >= 353 else \
|
||||||
|
('entity_id', 'x', 'y', 'z')
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
||||||
target = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
target = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
|
@ -17,6 +17,20 @@ class MapPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'map'
|
packet_name = 'map'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fields(self):
|
||||||
|
fields = 'id', 'scale', 'icons', 'width', 'height', 'pixels'
|
||||||
|
if self.context.protocol_version >= 107:
|
||||||
|
fields += 'is_tracking_position',
|
||||||
|
if self.context.protocol_version >= 452:
|
||||||
|
fields += 'is_locked',
|
||||||
|
return fields
|
||||||
|
|
||||||
|
def field_string(self, field):
|
||||||
|
if field == 'pixels' and isinstance(self.pixels, bytearray):
|
||||||
|
return 'bytearray(...)'
|
||||||
|
return super(MapPacket, self).field_string(field)
|
||||||
|
|
||||||
class MapIcon(MutableRecord):
|
class MapIcon(MutableRecord):
|
||||||
__slots__ = 'type', 'direction', 'location', 'display_name'
|
__slots__ = 'type', 'direction', 'location', 'display_name'
|
||||||
|
|
||||||
@ -43,11 +57,11 @@ class MapPacket(Packet):
|
|||||||
class MapSet(object):
|
class MapSet(object):
|
||||||
__slots__ = 'maps_by_id'
|
__slots__ = 'maps_by_id'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, *maps):
|
||||||
self.maps_by_id = dict()
|
self.maps_by_id = {map.id: map for map in maps}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
maps = (str(map) for map in self.maps_by_id.values())
|
maps = (repr(map) for map in self.maps_by_id.values())
|
||||||
return 'MapSet(%s)' % ', '.join(maps)
|
return 'MapSet(%s)' % ', '.join(maps)
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
@ -143,9 +157,3 @@ class MapPacket(Packet):
|
|||||||
UnsignedByte.send(self.offset[0], packet_buffer) # x
|
UnsignedByte.send(self.offset[0], packet_buffer) # x
|
||||||
UnsignedByte.send(self.offset[1], packet_buffer) # z
|
UnsignedByte.send(self.offset[1], packet_buffer) # z
|
||||||
VarIntPrefixedByteArray.send(self.pixels, packet_buffer)
|
VarIntPrefixedByteArray.send(self.pixels, packet_buffer)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '%sMapPacket(%s)' % (
|
|
||||||
('0x%02X ' % self.id) if self.id is not None else '',
|
|
||||||
', '.join('%s=%r' % (k, v) for (k, v) in self.__dict__.items()
|
|
||||||
if k not in ('pixels', '_context', 'id', 'definition')))
|
|
||||||
|
@ -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 (
|
||||||
String, Boolean, UUID, VarInt,
|
String, Boolean, UUID, VarInt, MutableRecord,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -20,21 +20,24 @@ class PlayerListItemPacket(Packet):
|
|||||||
|
|
||||||
packet_name = "player list item"
|
packet_name = "player list item"
|
||||||
|
|
||||||
|
fields = 'action_type', 'actions'
|
||||||
|
|
||||||
|
def field_string(self, field):
|
||||||
|
if field == 'action_type':
|
||||||
|
return self.action_type.__name__
|
||||||
|
return super(PlayerListItemPacket, self).field_string(field)
|
||||||
|
|
||||||
class PlayerList(object):
|
class PlayerList(object):
|
||||||
__slots__ = 'players_by_uuid'
|
__slots__ = 'players_by_uuid'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, *items):
|
||||||
self.players_by_uuid = dict()
|
self.players_by_uuid = {item.uuid: item for item in items}
|
||||||
|
|
||||||
class PlayerListItem(object):
|
class PlayerListItem(MutableRecord):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'uuid', 'name', 'properties', 'gamemode', 'ping', 'display_name')
|
'uuid', 'name', 'properties', 'gamemode', 'ping', 'display_name')
|
||||||
|
|
||||||
def __init__(self, **kwds):
|
class PlayerProperty(MutableRecord):
|
||||||
for key, val in kwds.items():
|
|
||||||
setattr(self, key, val)
|
|
||||||
|
|
||||||
class PlayerProperty(object):
|
|
||||||
__slots__ = 'name', 'value', 'signature'
|
__slots__ = 'name', 'value', 'signature'
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
@ -46,33 +49,44 @@ class PlayerListItemPacket(Packet):
|
|||||||
else:
|
else:
|
||||||
self.signature = None
|
self.signature = None
|
||||||
|
|
||||||
class Action(object):
|
def send(self, packet_buffer):
|
||||||
__slots__ = 'uuid'
|
String.send(self.name, packet_buffer)
|
||||||
|
String.send(self.value, packet_buffer)
|
||||||
|
if self.signature is not None:
|
||||||
|
Boolean.send(True, packet_buffer)
|
||||||
|
String.send(self.signature, packet_buffer)
|
||||||
|
else:
|
||||||
|
Boolean.send(False, packet_buffer)
|
||||||
|
|
||||||
|
class Action(MutableRecord):
|
||||||
|
__slots__ = 'uuid',
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
self.uuid = UUID.read(file_object)
|
self.uuid = UUID.read(file_object)
|
||||||
self._read(file_object)
|
self._read(file_object)
|
||||||
|
|
||||||
|
def send(self, packet_buffer):
|
||||||
|
UUID.send(self.uuid, packet_buffer)
|
||||||
|
self._send(packet_buffer)
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'This abstract method must be overridden in a subclass.')
|
'This abstract method must be overridden in a subclass.')
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'This abstract method must be overridden in a subclass.')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def type_from_id(cls, action_id):
|
def type_from_id(cls, action_id):
|
||||||
subcls = {
|
for subcls in cls.__subclasses__():
|
||||||
0: PlayerListItemPacket.AddPlayerAction,
|
if subcls.action_id == action_id:
|
||||||
1: PlayerListItemPacket.UpdateGameModeAction,
|
|
||||||
2: PlayerListItemPacket.UpdateLatencyAction,
|
|
||||||
3: PlayerListItemPacket.UpdateDisplayNameAction,
|
|
||||||
4: PlayerListItemPacket.RemovePlayerAction
|
|
||||||
}.get(action_id)
|
|
||||||
if subcls is None:
|
|
||||||
raise ValueError("Unknown player list action ID: %s."
|
|
||||||
% action_id)
|
|
||||||
return subcls
|
return subcls
|
||||||
|
raise ValueError("Unknown player list action ID: %s." % action_id)
|
||||||
|
|
||||||
class AddPlayerAction(Action):
|
class AddPlayerAction(Action):
|
||||||
__slots__ = 'name', 'properties', 'gamemode', 'ping', 'display_name'
|
__slots__ = 'name', 'properties', 'gamemode', 'ping', 'display_name'
|
||||||
|
action_id = 0
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
self.name = String.read(file_object)
|
self.name = String.read(file_object)
|
||||||
@ -90,6 +104,19 @@ class PlayerListItemPacket(Packet):
|
|||||||
else:
|
else:
|
||||||
self.display_name = None
|
self.display_name = None
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
String.send(self.name, packet_buffer)
|
||||||
|
VarInt.send(len(self.properties), packet_buffer)
|
||||||
|
for property in self.properties:
|
||||||
|
property.send(packet_buffer)
|
||||||
|
VarInt.send(self.gamemode, packet_buffer)
|
||||||
|
VarInt.send(self.ping, packet_buffer)
|
||||||
|
if self.display_name is not None:
|
||||||
|
Boolean.send(True, packet_buffer)
|
||||||
|
String.send(self.display_name, packet_buffer)
|
||||||
|
else:
|
||||||
|
Boolean.send(False, packet_buffer)
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
player = PlayerListItemPacket.PlayerListItem(
|
player = PlayerListItemPacket.PlayerListItem(
|
||||||
uuid=self.uuid,
|
uuid=self.uuid,
|
||||||
@ -102,10 +129,14 @@ class PlayerListItemPacket(Packet):
|
|||||||
|
|
||||||
class UpdateGameModeAction(Action):
|
class UpdateGameModeAction(Action):
|
||||||
__slots__ = 'gamemode'
|
__slots__ = 'gamemode'
|
||||||
|
action_id = 1
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
self.gamemode = VarInt.read(file_object)
|
self.gamemode = VarInt.read(file_object)
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
VarInt.send(self.gamemode, packet_buffer)
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
player = player_list.players_by_uuid.get(self.uuid)
|
player = player_list.players_by_uuid.get(self.uuid)
|
||||||
if player:
|
if player:
|
||||||
@ -113,10 +144,14 @@ class PlayerListItemPacket(Packet):
|
|||||||
|
|
||||||
class UpdateLatencyAction(Action):
|
class UpdateLatencyAction(Action):
|
||||||
__slots__ = 'ping'
|
__slots__ = 'ping'
|
||||||
|
action_id = 2
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
self.ping = VarInt.read(file_object)
|
self.ping = VarInt.read(file_object)
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
VarInt.send(self.ping, packet_buffer)
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
player = player_list.players_by_uuid.get(self.uuid)
|
player = player_list.players_by_uuid.get(self.uuid)
|
||||||
if player:
|
if player:
|
||||||
@ -124,6 +159,7 @@ class PlayerListItemPacket(Packet):
|
|||||||
|
|
||||||
class UpdateDisplayNameAction(Action):
|
class UpdateDisplayNameAction(Action):
|
||||||
__slots__ = 'display_name'
|
__slots__ = 'display_name'
|
||||||
|
action_id = 3
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
has_display_name = Boolean.read(file_object)
|
has_display_name = Boolean.read(file_object)
|
||||||
@ -132,15 +168,27 @@ class PlayerListItemPacket(Packet):
|
|||||||
else:
|
else:
|
||||||
self.display_name = None
|
self.display_name = None
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
if self.display_name is not None:
|
||||||
|
Boolean.send(True, packet_buffer)
|
||||||
|
String.send(self.display_name, packet_buffer)
|
||||||
|
else:
|
||||||
|
Boolean.send(False, packet_buffer)
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
player = player_list.players_by_uuid.get(self.uuid)
|
player = player_list.players_by_uuid.get(self.uuid)
|
||||||
if player:
|
if player:
|
||||||
player.display_name = self.display_name
|
player.display_name = self.display_name
|
||||||
|
|
||||||
class RemovePlayerAction(Action):
|
class RemovePlayerAction(Action):
|
||||||
|
action_id = 4
|
||||||
|
|
||||||
def _read(self, file_object):
|
def _read(self, file_object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _send(self, packet_buffer):
|
||||||
|
pass
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
if self.uuid in player_list.players_by_uuid:
|
if self.uuid in player_list.players_by_uuid:
|
||||||
del player_list.players_by_uuid[self.uuid]
|
del player_list.players_by_uuid[self.uuid]
|
||||||
@ -155,9 +203,12 @@ class PlayerListItemPacket(Packet):
|
|||||||
action.read(file_object)
|
action.read(file_object)
|
||||||
self.actions.append(action)
|
self.actions.append(action)
|
||||||
|
|
||||||
|
def write_fields(self, packet_buffer):
|
||||||
|
VarInt.send(self.action_type.action_id, packet_buffer)
|
||||||
|
VarInt.send(len(self.actions), packet_buffer)
|
||||||
|
for action in self.actions:
|
||||||
|
action.send(packet_buffer)
|
||||||
|
|
||||||
def apply(self, player_list):
|
def apply(self, player_list):
|
||||||
for action in self.actions:
|
for action in self.actions:
|
||||||
action.apply(player_list)
|
action.apply(player_list)
|
||||||
|
|
||||||
def write_fields(self, packet_buffer):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Double, Float, Byte, VarInt, BitFieldEnum, PositionAndLook
|
Double, Float, Byte, VarInt, BitFieldEnum, Vector, Direction,
|
||||||
|
PositionAndLook, multi_attribute_alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -30,6 +31,18 @@ class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
|
|||||||
{'teleport_id': VarInt} if context.protocol_version >= 107 else {},
|
{'teleport_id': VarInt} if context.protocol_version >= 107 else {},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
||||||
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
|
|
||||||
|
# Access the 'yaw', 'pitch' fields as a Direction tuple.
|
||||||
|
look = multi_attribute_alias(Direction, 'yaw', 'pitch')
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z', 'yaw', 'pitch' fields as a PositionAndLook.
|
||||||
|
# NOTE: modifying the object retrieved from this property will not change
|
||||||
|
# the packet; it can only be changed by attribute or property assignment.
|
||||||
|
position_and_look = multi_attribute_alias(
|
||||||
|
PositionAndLook, 'x', 'y', 'z', 'yaw', 'pitch')
|
||||||
|
|
||||||
field_enum = classmethod(
|
field_enum = classmethod(
|
||||||
lambda cls, field, context: cls if field == 'flags' else None)
|
lambda cls, field, context: cls if field == 'flags' else None)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from minecraft.networking.types.utility import descriptor
|
|||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, UUID, Byte, Double, Integer, Angle, Short, Enum, Vector,
|
VarInt, UUID, Byte, Double, Integer, Angle, Short, Enum, Vector,
|
||||||
PositionAndLook, attribute_alias, multi_attribute_alias,
|
Direction, PositionAndLook, attribute_alias, multi_attribute_alias,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ class SpawnObjectPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'spawn object'
|
packet_name = 'spawn object'
|
||||||
|
|
||||||
fields = ('entity_id', 'object_uuid', 'type_id',
|
fields = ('entity_id', 'object_uuid', 'type_id', 'x', 'y', 'z', 'pitch',
|
||||||
'x', 'y', 'z', 'pitch', 'yaw')
|
'yaw', 'data', 'velocity_x', 'velocity_y', 'velocity_z')
|
||||||
|
|
||||||
@descriptor
|
@descriptor
|
||||||
def EntityType(desc, self, cls): # pylint: disable=no-self-argument
|
def EntityType(desc, self, cls): # pylint: disable=no-self-argument
|
||||||
@ -152,13 +152,19 @@ class SpawnObjectPacket(Packet):
|
|||||||
def type(self):
|
def type(self):
|
||||||
del self.type_id
|
del self.type_id
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z' fields as a Vector.
|
||||||
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
|
|
||||||
|
# Access the 'yaw', 'pitch' fields as a Direction.
|
||||||
|
look = multi_attribute_alias(Direction, 'yaw', 'pitch')
|
||||||
|
|
||||||
|
# Access the 'x', 'y', 'z', 'pitch', 'yaw' fields as a PositionAndLook.
|
||||||
# NOTE: modifying the object retrieved from this property will not change
|
# NOTE: modifying the object retrieved from this property will not change
|
||||||
# the packet; it can only be changed by attribute or property assignment.
|
# the packet; it can only be changed by attribute or property assignment.
|
||||||
position_and_look = multi_attribute_alias(
|
position_and_look = multi_attribute_alias(
|
||||||
PositionAndLook, x='x', y='y', z='z', yaw='yaw', pitch='pitch')
|
PositionAndLook, x='x', y='y', z='z', yaw='yaw', pitch='pitch')
|
||||||
|
|
||||||
|
# Access the 'velocity_{x,y,z}' fields as a Vector.
|
||||||
velocity = multi_attribute_alias(
|
velocity = multi_attribute_alias(
|
||||||
Vector, 'velocity_x', 'velocity_y', 'velocity_z')
|
Vector, 'velocity_x', 'velocity_y', 'velocity_z')
|
||||||
|
|
||||||
|
@ -112,8 +112,9 @@ class Packet(object):
|
|||||||
str = '0x%02X %s' % (self.id, str)
|
str = '0x%02X %s' % (self.id, str)
|
||||||
fields = self.fields
|
fields = self.fields
|
||||||
if fields is not None:
|
if fields is not None:
|
||||||
str = '%s(%s)' % (str, ', '.join('%s=%s' %
|
inner_str = ', '.join('%s=%s' % (a, self.field_string(a))
|
||||||
(a, self.field_string(a)) for a in fields))
|
for a in fields if hasattr(self, a))
|
||||||
|
str = '%s(%s)' % (str, inner_str)
|
||||||
return str
|
return str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -4,7 +4,8 @@ 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
|
RelativeHand, BlockFace, Vector, Direction, PositionAndLook,
|
||||||
|
multi_attribute_alias
|
||||||
)
|
)
|
||||||
|
|
||||||
from .client_settings_packet import ClientSettingsPacket
|
from .client_settings_packet import ClientSettingsPacket
|
||||||
@ -98,6 +99,19 @@ class PositionAndLookPacket(Packet):
|
|||||||
{'pitch': Float},
|
{'pitch': Float},
|
||||||
{'on_ground': Boolean}]
|
{'on_ground': Boolean}]
|
||||||
|
|
||||||
|
# Access the 'x', 'feet_y', 'z' fields as a Vector tuple.
|
||||||
|
position = multi_attribute_alias(Vector, 'x', 'feet_y', 'z')
|
||||||
|
|
||||||
|
# Access the 'yaw', 'pitch' fields as a Direction tuple.
|
||||||
|
look = multi_attribute_alias(Direction, 'yaw', 'pitch')
|
||||||
|
|
||||||
|
# Access the 'x', 'feet_y', 'z', 'yaw', 'pitch' fields as a
|
||||||
|
# PositionAndLook.
|
||||||
|
# NOTE: modifying the object retrieved from this property will not change
|
||||||
|
# the packet; it can only be changed by attribute or property assignment.
|
||||||
|
position_and_look = multi_attribute_alias(
|
||||||
|
PositionAndLook, 'x', 'feet_y', 'z', 'yaw', 'pitch')
|
||||||
|
|
||||||
|
|
||||||
class TeleportConfirmPacket(Packet):
|
class TeleportConfirmPacket(Packet):
|
||||||
# Note: added between protocol versions 47 and 107.
|
# Note: added between protocol versions 47 and 107.
|
||||||
|
@ -8,7 +8,7 @@ from itertools import chain
|
|||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Vector', 'MutableRecord', 'PositionAndLook', 'descriptor',
|
'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor',
|
||||||
'attribute_alias', 'multi_attribute_alias',
|
'attribute_alias', 'multi_attribute_alias',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,8 +53,9 @@ class Vector(namedtuple('BaseVector', ('x', 'y', 'z'))):
|
|||||||
|
|
||||||
|
|
||||||
class MutableRecord(object):
|
class MutableRecord(object):
|
||||||
"""An abstract base class providing namedtuple-like repr(), ==, and hash()
|
"""An abstract base class providing namedtuple-like repr(), ==, hash(), and
|
||||||
implementations for types containing mutable fields given by __slots__.
|
iter(), implementations for types containing mutable fields given by
|
||||||
|
__slots__.
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@ -64,19 +65,27 @@ class MutableRecord(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (type(self).__name__, ', '.join(
|
return '%s(%s)' % (type(self).__name__, ', '.join(
|
||||||
'%s=%r' % (a, getattr(self, a)) for a in self.__slots__))
|
'%s=%r' % (a, getattr(self, a)) for a in self._all_slots()))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return type(self) is type(other) and \
|
return type(self) is type(other) and all(
|
||||||
all(getattr(self, a) == getattr(other, a) for a in self.__slots__)
|
getattr(self, a) == getattr(other, a) for a in self._all_slots())
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
values = tuple(getattr(self, a, None) for a in self.__slots__)
|
values = tuple(getattr(self, a, None) for a in self._all_slots())
|
||||||
return hash((type(self), values))
|
return hash((type(self), values))
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(getattr(self, a) for a in self._all_slots())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _all_slots(cls):
|
||||||
|
return tuple(f for c in reversed(cls.__mro__)
|
||||||
|
for f in getattr(c, '__slots__', ()))
|
||||||
|
|
||||||
|
|
||||||
def attribute_alias(name):
|
def attribute_alias(name):
|
||||||
"""An attribute descriptor that redirects access to a different attribute
|
"""An attribute descriptor that redirects access to a different attribute
|
||||||
@ -97,10 +106,18 @@ def multi_attribute_alias(container, *arg_names, **kwd_names):
|
|||||||
argument to its constructor, and accessible as its 'n'th iterable
|
argument to its constructor, and accessible as its 'n'th iterable
|
||||||
element.
|
element.
|
||||||
|
|
||||||
|
As a special case, 'tuple' may be given as the 'container' when there
|
||||||
|
are positional arguments, and (even though the tuple constructor does
|
||||||
|
not take positional arguments), the arguments will be aliased to the
|
||||||
|
corresponding positions in a tuple.
|
||||||
|
|
||||||
The name in 'kwd_names' mapped to by the key 'k' is the parent attribute
|
The name in 'kwd_names' mapped to by the key 'k' is the parent attribute
|
||||||
that will be aliased to the field of 'container' settable by the keyword
|
that will be aliased to the field of 'container' settable by the keyword
|
||||||
argument 'k' to its constructor, and accessible as its 'k' attribute.
|
argument 'k' to its constructor, and accessible as its 'k' attribute.
|
||||||
"""
|
"""
|
||||||
|
if container is tuple:
|
||||||
|
container = lambda *args: args # noqa: E731
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alias(self):
|
def alias(self):
|
||||||
return container(
|
return container(
|
||||||
|
@ -149,7 +149,8 @@ class PacketEnumTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(ExamplePacket(ConnectionContext(), alpha=0, beta=0, gamma=0)),
|
str(ExamplePacket(ConnectionContext(), alpha=0, beta=0, gamma=0)),
|
||||||
'0x00 ExamplePacket(alpha=ZERO, beta=0, gamma=0)')
|
'0x00 ExamplePacket(alpha=ZERO, beta=0, gamma=0)'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestReadWritePackets(unittest.TestCase):
|
class TestReadWritePackets(unittest.TestCase):
|
||||||
@ -163,16 +164,43 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
Record(-35, -127, 95), Record(11, 113, -8)],
|
Record(-35, -127, 95), Record(11, 113, -8)],
|
||||||
player_motion=Vector(4, 5, 0))
|
player_motion=Vector(4, 5, 0))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
'ExplosionPacket(x=787, y=-37, z=0, radius=15, records=['
|
||||||
|
'Record(-14, -116, -5), Record(-77, 34, -36), '
|
||||||
|
'Record(-35, -127, 95), Record(11, 113, -8)], '
|
||||||
|
'player_motion_x=4, player_motion_y=5, player_motion_z=0)'
|
||||||
|
)
|
||||||
|
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
def test_combat_event_packet(self):
|
def test_combat_event_packet(self):
|
||||||
packet = clientbound.play.CombatEventPacket()
|
packet = clientbound.play.CombatEventPacket(
|
||||||
for event in (
|
event=clientbound.play.CombatEventPacket.EnterCombatEvent())
|
||||||
packet.EnterCombatEvent(),
|
self.assertEqual(
|
||||||
packet.EndCombatEvent(duration=415, entity_id=91063502),
|
str(packet),
|
||||||
packet.EntityDeadEvent(player_id=178, entity_id=36, message='RIP'),
|
'CombatEventPacket(event=EnterCombatEvent())'
|
||||||
):
|
)
|
||||||
packet.event = event
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
|
packet = clientbound.play.CombatEventPacket(
|
||||||
|
event=clientbound.play.CombatEventPacket.EndCombatEvent(
|
||||||
|
duration=415, entity_id=91063502))
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
'CombatEventPacket(event=EndCombatEvent('
|
||||||
|
'duration=415, entity_id=91063502))'
|
||||||
|
)
|
||||||
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
|
packet = clientbound.play.CombatEventPacket(
|
||||||
|
event=clientbound.play.CombatEventPacket.EntityDeadEvent(
|
||||||
|
player_id=178, entity_id=36, message='RIP'))
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
"CombatEventPacket(event=EntityDeadEvent("
|
||||||
|
"player_id=178, entity_id=36, message='RIP'))"
|
||||||
|
)
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
def test_multi_block_change_packet(self):
|
def test_multi_block_change_packet(self):
|
||||||
@ -186,8 +214,16 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
self.assertEqual(packet.records[0].blockMeta, 13)
|
self.assertEqual(packet.records[0].blockMeta, 13)
|
||||||
self.assertEqual(packet.records[0].blockStateId, 909)
|
self.assertEqual(packet.records[0].blockStateId, 909)
|
||||||
self.assertEqual(packet.records[0].position, Vector(1, 2, 3))
|
self.assertEqual(packet.records[0].position, Vector(1, 2, 3))
|
||||||
self.assertEqual(packet.records[0], packet.records[1])
|
self.assertEqual(packet.chunk_pos, (packet.chunk_x, packet.chunk_z))
|
||||||
self.assertEqual(packet.records[1], packet.records[2])
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
'MultiBlockChangePacket(chunk_x=167, chunk_z=15, records=['
|
||||||
|
'Record(x=1, y=2, z=3, block_state_id=909), '
|
||||||
|
'Record(x=1, y=2, z=3, block_state_id=909), '
|
||||||
|
'Record(x=1, y=2, z=3, block_state_id=909)])'
|
||||||
|
)
|
||||||
|
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
def test_spawn_object_packet(self):
|
def test_spawn_object_packet(self):
|
||||||
@ -221,6 +257,23 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
self.assertEqual(packet.velocity, velocity)
|
self.assertEqual(packet.velocity, velocity)
|
||||||
self.assertEqual(packet.type, type_name)
|
self.assertEqual(packet.type, type_name)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
"0x%02X SpawnObjectPacket(entity_id=49846, "
|
||||||
|
"object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', "
|
||||||
|
"type_id=EGG, x=68.0, y=38.0, z=76.0, pitch=180, yaw=263.494, "
|
||||||
|
"data=1, velocity_x=21, velocity_y=55, velocity_z=41)"
|
||||||
|
% packet.id if protocol_version >= 100 else
|
||||||
|
"0x%02X SpawnObjectPacket(entity_id=49846, "
|
||||||
|
"object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', "
|
||||||
|
"type_id=EGG, x=68, y=38, z=76, pitch=180, yaw=263.494, "
|
||||||
|
"data=1, velocity_x=21, velocity_y=55, velocity_z=41)"
|
||||||
|
% packet.id if protocol_version >= 49 else
|
||||||
|
"0x%02X SpawnObjectPacket(entity_id=49846, type_id=EGG, "
|
||||||
|
"x=68, y=38, z=76, pitch=180, yaw=263.494, data=1, "
|
||||||
|
"velocity_x=21, velocity_y=55, velocity_z=41)" % packet.id
|
||||||
|
)
|
||||||
|
|
||||||
packet2 = clientbound.play.SpawnObjectPacket(
|
packet2 = clientbound.play.SpawnObjectPacket(
|
||||||
context=context, position_and_look=pos_look,
|
context=context, position_and_look=pos_look,
|
||||||
velocity=velocity, type=type_name,
|
velocity=velocity, type=type_name,
|
||||||
@ -254,17 +307,25 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
if context.protocol_version >= 95:
|
if context.protocol_version >= 95:
|
||||||
packet.sound_category = \
|
packet.sound_category = \
|
||||||
clientbound.play.SoundEffectPacket.SoundCategory.NEUTRAL
|
clientbound.play.SoundEffectPacket.SoundCategory.NEUTRAL
|
||||||
|
|
||||||
self._test_read_write_packet(packet, context)
|
self._test_read_write_packet(packet, context)
|
||||||
|
|
||||||
def test_face_player_packet(self):
|
def test_face_player_packet(self):
|
||||||
for protocol_version in TEST_VERSIONS:
|
for protocol_version in TEST_VERSIONS:
|
||||||
context = ConnectionContext(protocol_version=protocol_version)
|
context = ConnectionContext(protocol_version=protocol_version)
|
||||||
|
|
||||||
packet = clientbound.play.FacePlayerPacket()
|
packet = clientbound.play.FacePlayerPacket(context)
|
||||||
packet.target = 1.0, -2.0, 3.5
|
packet.target = 1.0, -2.0, 3.5
|
||||||
packet.entity_id = None
|
packet.entity_id = None
|
||||||
if protocol_version >= 353:
|
if protocol_version >= 353:
|
||||||
packet.origin = OriginPoint.EYES
|
packet.origin = OriginPoint.EYES
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
"0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, "
|
||||||
|
"entity_id=None)" % packet.id if protocol_version >= 353 else
|
||||||
|
"0x%02X FacePlayerPacket(entity_id=None, x=1.0, y=-2.0, z=3.5)"
|
||||||
|
% packet.id
|
||||||
|
)
|
||||||
self._test_read_write_packet(packet, context)
|
self._test_read_write_packet(packet, context)
|
||||||
|
|
||||||
packet.entity_id = 123
|
packet.entity_id = 123
|
||||||
@ -272,6 +333,13 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
packet.entity_origin = OriginPoint.FEET
|
packet.entity_origin = OriginPoint.FEET
|
||||||
else:
|
else:
|
||||||
del packet.target
|
del packet.target
|
||||||
|
self.assertEqual(
|
||||||
|
str(packet),
|
||||||
|
"0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, "
|
||||||
|
"entity_id=123, entity_origin=FEET)" % packet.id
|
||||||
|
if protocol_version >= 353 else
|
||||||
|
"0x%02X FacePlayerPacket(entity_id=123)" % packet.id
|
||||||
|
)
|
||||||
self._test_read_write_packet(packet, context)
|
self._test_read_write_packet(packet, context)
|
||||||
|
|
||||||
def _test_read_write_packet(self, packet_in, context=None, **kwargs):
|
def _test_read_write_packet(self, packet_in, context=None, **kwargs):
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from minecraft.networking.types import (UUID, VarInt, String, Boolean)
|
from minecraft.networking.types import (UUID, VarInt)
|
||||||
from minecraft.networking.packets import PacketBuffer
|
from minecraft.networking.packets import PacketBuffer
|
||||||
from minecraft.networking.packets.clientbound.play import (
|
from minecraft.networking.packets.clientbound.play import (
|
||||||
PlayerPositionAndLookPacket, PlayerListItemPacket, MapPacket
|
PlayerPositionAndLookPacket, PlayerListItemPacket, MapPacket
|
||||||
)
|
)
|
||||||
from minecraft.networking.connection import ConnectionContext
|
from minecraft.networking.connection import ConnectionContext
|
||||||
|
|
||||||
|
from tests.test_packets import TEST_VERSIONS
|
||||||
|
|
||||||
|
|
||||||
class PlayerPositionAndLookTest(unittest.TestCase):
|
class PlayerPositionAndLookTest(unittest.TestCase):
|
||||||
|
|
||||||
@ -141,36 +143,35 @@ class PlayerListItemTest(unittest.TestCase):
|
|||||||
PlayerListItemPacket().read(packet_buffer)
|
PlayerListItemPacket().read(packet_buffer)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_add_player_packet(display_name=True):
|
def make_add_player_packet(context, display_name=True):
|
||||||
packet_buffer = PacketBuffer()
|
packet_buffer = PacketBuffer()
|
||||||
|
PlayerListItemPacket(
|
||||||
VarInt.send(0, packet_buffer) # action_id
|
context=context,
|
||||||
VarInt.send(1, packet_buffer) # action count
|
action_type=PlayerListItemPacket.AddPlayerAction,
|
||||||
UUID.send(fake_uuid, packet_buffer) # uuid
|
actions=[
|
||||||
String.send("player", packet_buffer) # player name
|
PlayerListItemPacket.AddPlayerAction(
|
||||||
|
uuid=fake_uuid,
|
||||||
VarInt.send(2, packet_buffer) # number of properties
|
name='goodmonson',
|
||||||
String.send("property1", packet_buffer)
|
properties=[
|
||||||
String.send("value1", packet_buffer)
|
PlayerListItemPacket.PlayerProperty(
|
||||||
Boolean.send(False, packet_buffer) # is signed
|
name='property1', value='value1', signature=None),
|
||||||
String.send("property2", packet_buffer)
|
PlayerListItemPacket.PlayerProperty(
|
||||||
String.send("value2", packet_buffer)
|
name='property2', value='value2', signature='gm')
|
||||||
Boolean.send(True, packet_buffer) # is signed
|
],
|
||||||
String.send("signature", packet_buffer)
|
gamemode=42,
|
||||||
|
ping=69,
|
||||||
VarInt.send(42, packet_buffer) # game mode
|
display_name='Goodmonson' if display_name else None
|
||||||
VarInt.send(69, packet_buffer) # ping
|
),
|
||||||
Boolean.send(display_name, packet_buffer) # has display name
|
],
|
||||||
if display_name:
|
).write_fields(packet_buffer)
|
||||||
String.send("display", packet_buffer) # display name
|
|
||||||
|
|
||||||
packet_buffer.reset_cursor()
|
packet_buffer.reset_cursor()
|
||||||
return packet_buffer
|
return packet_buffer
|
||||||
|
|
||||||
def test_add_player_action(self):
|
def test_add_player_action(self):
|
||||||
|
for protocol_version in TEST_VERSIONS:
|
||||||
|
context = ConnectionContext(protocol_version=protocol_version)
|
||||||
player_list = PlayerListItemPacket.PlayerList()
|
player_list = PlayerListItemPacket.PlayerList()
|
||||||
|
packet_buffer = self.make_add_player_packet(context)
|
||||||
packet_buffer = self.make_add_player_packet()
|
|
||||||
|
|
||||||
packet = PlayerListItemPacket()
|
packet = PlayerListItemPacket()
|
||||||
packet.read(packet_buffer)
|
packet.read(packet_buffer)
|
||||||
@ -179,66 +180,95 @@ class PlayerListItemTest(unittest.TestCase):
|
|||||||
self.assertIn(fake_uuid, player_list.players_by_uuid)
|
self.assertIn(fake_uuid, player_list.players_by_uuid)
|
||||||
player = player_list.players_by_uuid[fake_uuid]
|
player = player_list.players_by_uuid[fake_uuid]
|
||||||
|
|
||||||
self.assertEqual(player.name, "player")
|
self.assertEqual(player.name, 'goodmonson')
|
||||||
self.assertEqual(player.properties[0].name, "property1")
|
self.assertEqual(player.properties[0].name, 'property1')
|
||||||
self.assertIsNone(player.properties[0].signature)
|
self.assertIsNone(player.properties[0].signature)
|
||||||
self.assertEqual(player.properties[1].value, "value2")
|
self.assertEqual(player.properties[1].value, 'value2')
|
||||||
self.assertEqual(player.properties[1].signature, "signature")
|
self.assertEqual(player.properties[1].signature, 'gm')
|
||||||
self.assertEqual(player.gamemode, 42)
|
self.assertEqual(player.gamemode, 42)
|
||||||
self.assertEqual(player.ping, 69)
|
self.assertEqual(player.ping, 69)
|
||||||
self.assertEqual(player.display_name, "display")
|
self.assertEqual(player.display_name, 'Goodmonson')
|
||||||
|
|
||||||
@staticmethod
|
def read_and_apply(self, context, packet_buffer, player_list):
|
||||||
def make_action_base(action_id):
|
|
||||||
packet_buffer = PacketBuffer()
|
|
||||||
VarInt.send(action_id, packet_buffer)
|
|
||||||
VarInt.send(1, packet_buffer) # action count
|
|
||||||
UUID.send(fake_uuid, packet_buffer)
|
|
||||||
|
|
||||||
return packet_buffer
|
|
||||||
|
|
||||||
def read_and_apply(self, packet_buffer, player_list):
|
|
||||||
packet_buffer.reset_cursor()
|
packet_buffer.reset_cursor()
|
||||||
packet = PlayerListItemPacket()
|
packet = PlayerListItemPacket(context)
|
||||||
packet.read(packet_buffer)
|
packet.read(packet_buffer)
|
||||||
packet.apply(player_list)
|
packet.apply(player_list)
|
||||||
|
|
||||||
def test_add_and_others(self):
|
def test_add_and_others(self):
|
||||||
|
for protocol_version in TEST_VERSIONS:
|
||||||
|
context = ConnectionContext(protocol_version=protocol_version)
|
||||||
|
|
||||||
player_list = PlayerListItemPacket.PlayerList()
|
player_list = PlayerListItemPacket.PlayerList()
|
||||||
by_uuid = player_list.players_by_uuid
|
by_uuid = player_list.players_by_uuid
|
||||||
|
|
||||||
packet_buffer = self.make_add_player_packet(display_name=False)
|
packet_buffer = self.make_add_player_packet(
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
context, display_name=False)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertEqual(by_uuid[fake_uuid].gamemode, 42)
|
self.assertEqual(by_uuid[fake_uuid].gamemode, 42)
|
||||||
self.assertEqual(by_uuid[fake_uuid].ping, 69)
|
self.assertEqual(by_uuid[fake_uuid].ping, 69)
|
||||||
self.assertIsNone(by_uuid[fake_uuid].display_name)
|
self.assertIsNone(by_uuid[fake_uuid].display_name)
|
||||||
|
|
||||||
# Change the game mode
|
# Change the game mode
|
||||||
packet_buffer = self.make_action_base(1)
|
packet_buffer = PacketBuffer()
|
||||||
VarInt.send(43, packet_buffer) # gamemode
|
PlayerListItemPacket(
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
context=context,
|
||||||
|
action_type=PlayerListItemPacket.UpdateGameModeAction,
|
||||||
|
actions=[
|
||||||
|
PlayerListItemPacket.UpdateGameModeAction(
|
||||||
|
uuid=fake_uuid, gamemode=43),
|
||||||
|
],
|
||||||
|
).write_fields(packet_buffer)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertEqual(by_uuid[fake_uuid].gamemode, 43)
|
self.assertEqual(by_uuid[fake_uuid].gamemode, 43)
|
||||||
|
|
||||||
# Change the ping
|
# Change the ping
|
||||||
packet_buffer = self.make_action_base(2)
|
packet_buffer = PacketBuffer()
|
||||||
VarInt.send(70, packet_buffer) # ping
|
PlayerListItemPacket(
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
context=context,
|
||||||
|
action_type=PlayerListItemPacket.UpdateLatencyAction,
|
||||||
|
actions=[
|
||||||
|
PlayerListItemPacket.UpdateLatencyAction(
|
||||||
|
uuid=fake_uuid, ping=70),
|
||||||
|
],
|
||||||
|
).write_fields(packet_buffer)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertEqual(by_uuid[fake_uuid].ping, 70)
|
self.assertEqual(by_uuid[fake_uuid].ping, 70)
|
||||||
|
|
||||||
# Change the display name
|
# Change the display name
|
||||||
packet_buffer = self.make_action_base(3)
|
packet_buffer = PacketBuffer()
|
||||||
Boolean.send(True, packet_buffer)
|
PlayerListItemPacket(
|
||||||
String.send("display2", packet_buffer)
|
context=context,
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
action_type=PlayerListItemPacket.UpdateDisplayNameAction,
|
||||||
self.assertEqual(by_uuid[fake_uuid].display_name, "display2")
|
actions=[
|
||||||
|
PlayerListItemPacket.UpdateDisplayNameAction(
|
||||||
|
uuid=fake_uuid, display_name='Badmonson'),
|
||||||
|
],
|
||||||
|
).write_fields(packet_buffer)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
|
self.assertEqual(by_uuid[fake_uuid].display_name, 'Badmonson')
|
||||||
|
|
||||||
# Remove the display name
|
# Remove the display name
|
||||||
packet_buffer = self.make_action_base(3)
|
packet_buffer = PacketBuffer()
|
||||||
Boolean.send(False, packet_buffer)
|
PlayerListItemPacket(
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
context=context,
|
||||||
|
action_type=PlayerListItemPacket.UpdateDisplayNameAction,
|
||||||
|
actions=[
|
||||||
|
PlayerListItemPacket.UpdateDisplayNameAction(
|
||||||
|
uuid=fake_uuid, display_name=None),
|
||||||
|
],
|
||||||
|
).write_fields(packet_buffer)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertIsNone(by_uuid[fake_uuid].display_name)
|
self.assertIsNone(by_uuid[fake_uuid].display_name)
|
||||||
|
|
||||||
# Remove the player
|
# Remove the player
|
||||||
packet_buffer = self.make_action_base(4)
|
packet_buffer = PacketBuffer()
|
||||||
self.read_and_apply(packet_buffer, player_list)
|
PlayerListItemPacket(
|
||||||
|
context=context,
|
||||||
|
action_type=PlayerListItemPacket.RemovePlayerAction,
|
||||||
|
actions=[
|
||||||
|
PlayerListItemPacket.RemovePlayerAction(uuid=fake_uuid),
|
||||||
|
],
|
||||||
|
).write_fields(packet_buffer)
|
||||||
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertNotIn(fake_uuid, player_list.players_by_uuid)
|
self.assertNotIn(fake_uuid, player_list.players_by_uuid)
|
||||||
|
Loading…
Reference in New Issue
Block a user