mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-16 07:15:24 +01:00
Add serialisation and tests for Explosion, {Multi,}BlockChange, and CombatEvent packets.
This commit is contained in:
parent
92f2eff681
commit
709b80b539
@ -3,8 +3,8 @@ from minecraft.networking.packets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Integer, UnsignedByte, Byte, Boolean, UUID, Short, Position,
|
Integer, UnsignedByte, Byte, Boolean, UUID, Short, VarInt, Double, Float,
|
||||||
VarInt, Double, Float, String, Enum,
|
String, Enum,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .combat_event_packet import CombatEventPacket
|
from .combat_event_packet import CombatEventPacket
|
||||||
@ -13,6 +13,7 @@ from .player_list_item_packet import PlayerListItemPacket
|
|||||||
from .player_position_and_look_packet import PlayerPositionAndLookPacket
|
from .player_position_and_look_packet import PlayerPositionAndLookPacket
|
||||||
from .spawn_object_packet import SpawnObjectPacket
|
from .spawn_object_packet import SpawnObjectPacket
|
||||||
from .block_change_packet import BlockChangePacket, MultiBlockChangePacket
|
from .block_change_packet import BlockChangePacket, MultiBlockChangePacket
|
||||||
|
from .explosion_packet import ExplosionPacket
|
||||||
|
|
||||||
|
|
||||||
# Formerly known as state_playing_clientbound.
|
# Formerly known as state_playing_clientbound.
|
||||||
@ -176,42 +177,6 @@ class UpdateHealthPacket(Packet):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class ExplosionPacket(Packet):
|
|
||||||
@staticmethod
|
|
||||||
def get_id(context):
|
|
||||||
return 0x1D if context.protocol_version >= 345 else \
|
|
||||||
0x1C if context.protocol_version >= 332 else \
|
|
||||||
0x1D if context.protocol_version >= 318 else \
|
|
||||||
0x1C if context.protocol_version >= 80 else \
|
|
||||||
0x1B if context.protocol_version >= 67 else \
|
|
||||||
0x27
|
|
||||||
|
|
||||||
packet_name = 'explosion'
|
|
||||||
|
|
||||||
class Record(Position):
|
|
||||||
pass
|
|
||||||
|
|
||||||
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 = VarInt.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(self, socket, compression_threshold=None):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class PluginMessagePacket(AbstractPluginMessagePacket):
|
class PluginMessagePacket(AbstractPluginMessagePacket):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
from minecraft.networking.packets import (
|
from minecraft.networking.packets import Packet
|
||||||
Packet, PacketBuffer
|
|
||||||
)
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Integer, UnsignedByte, Position
|
VarInt, Integer, UnsignedByte, Position, Vector
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -16,23 +14,25 @@ class BlockChangePacket(Packet):
|
|||||||
0x23
|
0x23
|
||||||
|
|
||||||
packet_name = 'block change'
|
packet_name = 'block change'
|
||||||
|
definition = [
|
||||||
|
{'location': Position},
|
||||||
|
{'block_state_id': VarInt}]
|
||||||
|
block_state_id = 0
|
||||||
|
|
||||||
def read(self, file_object):
|
# For protocols < 347: an accessor for (block_state_id >> 4).
|
||||||
self.location = Position.read(file_object)
|
def blockId(self, block_id):
|
||||||
blockData = VarInt.read(file_object)
|
self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4)
|
||||||
if self.context.protocol_version >= 347:
|
blockId = property(lambda self: self.block_state_id >> 4, blockId)
|
||||||
# See comments on MultiBlockChangePacket.OpaqueRecord.
|
|
||||||
self.blockStateId = blockData
|
|
||||||
else:
|
|
||||||
self.blockId = (blockData >> 4)
|
|
||||||
self.blockMeta = (blockData & 0xF)
|
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
# For protocols < 347: an accessor for (block_state_id & 0xF).
|
||||||
packet_buffer = PacketBuffer()
|
def blockMeta(self, meta):
|
||||||
Position.send(self.location, packet_buffer)
|
self.block_state_id = (self.block_state_id & ~0xF) | (meta & 0xF)
|
||||||
blockData = ((self.blockId << 4) | (self.blockMeta & 0xF))
|
blockMeta = property(lambda self: self.block_state_id & 0xF, blockMeta)
|
||||||
VarInt.send(blockData)
|
|
||||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
# This alias is retained for backward compatibility.
|
||||||
|
def blockStateId(self, block_state_id):
|
||||||
|
self.block_state_id = block_state_id
|
||||||
|
blockStateId = property(lambda self: self.block_state_id, blockStateId)
|
||||||
|
|
||||||
|
|
||||||
class MultiBlockChangePacket(Packet):
|
class MultiBlockChangePacket(Packet):
|
||||||
@ -46,60 +46,66 @@ class MultiBlockChangePacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'multi block change'
|
packet_name = 'multi block change'
|
||||||
|
|
||||||
class BaseRecord(object):
|
class Record(object):
|
||||||
__slots__ = 'x', 'y', 'z'
|
__slots__ = 'x', 'y', 'z', 'block_state_id'
|
||||||
|
|
||||||
def __init__(self, horizontal_position, y_coordinate):
|
def __init__(self, **kwds):
|
||||||
self.x = (horizontal_position & 0xF0) >> 4
|
self.block_state_id = 0
|
||||||
self.y = y_coordinate
|
for attr, value in kwds.items():
|
||||||
self.z = (horizontal_position & 0x0F)
|
setattr(self, attr, value)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_subclass(cls, context):
|
|
||||||
return MultiBlockChangePacket.OpaqueRecord \
|
|
||||||
if context.protocol_version >= 347 else \
|
|
||||||
MultiBlockChangePacket.Record
|
|
||||||
|
|
||||||
class Record(BaseRecord):
|
|
||||||
__slots__ = 'blockId', 'blockMeta'
|
|
||||||
|
|
||||||
def __init__(self, h_position, y_coordinate, blockData):
|
|
||||||
super(MultiBlockChangePacket.Record, self).__init__(
|
|
||||||
h_position, y_coordinate)
|
|
||||||
self.blockId = (blockData >> 4)
|
|
||||||
self.blockMeta = (blockData & 0xF)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ('Record(x=%s, y=%s, z=%s, blockId=%s)'
|
return '%s(%s)' % (type(self).__name__, ', '.join(
|
||||||
% (self.x, self.y, self.z, self.blockId))
|
'%s=%r' % (a, getattr(self, a)) for a in self.__slots__))
|
||||||
|
|
||||||
'''The structure of the block data changed in protocol 347 (17w47b,
|
def __eq__(self, other):
|
||||||
between 1.12.2 and 1.13), which this class reflects: instead of a
|
return type(self) is type(other) and all(
|
||||||
separate blockId and blockMeta number, there is a single opaque
|
getattr(self, a) == getattr(other, a) for a in self.__slots__)
|
||||||
blockStateId whose meaning may change between minor versions.'''
|
|
||||||
class OpaqueRecord(BaseRecord):
|
|
||||||
__slots__ = 'blockStateId'
|
|
||||||
|
|
||||||
def __init__(self, h_position, y_coordinate, blockData):
|
# Access the 'x', 'y', 'z' fields as a Vector of ints.
|
||||||
super(MultiBlockChangePacket.OpaqueRecord, self).__init__(
|
def position(self, position):
|
||||||
h_position, y_coordinate)
|
self.x, self.y, self.z = position
|
||||||
self.blockStateId = blockData
|
position = property(lambda r: Vector(r.x, r.y, r.z), position)
|
||||||
|
|
||||||
def __repr__(self):
|
# For protocols < 347: an accessor for (block_state_id >> 4).
|
||||||
return ('OpaqueRecord(x=%s, y=%s, z=%s, blockStateId=%s)'
|
def blockId(self, block_id):
|
||||||
% (self.x, self.y, self.z, self.blockStateId))
|
self.block_state_id = self.block_state_id & 0xF | block_id << 4
|
||||||
|
blockId = property(lambda r: r.block_state_id >> 4, blockId)
|
||||||
|
|
||||||
|
# For protocols < 347: an accessor for (block_state_id & 0xF).
|
||||||
|
def blockMeta(self, meta):
|
||||||
|
self.block_state_id = self.block_state_id & ~0xF | meta & 0xF
|
||||||
|
blockMeta = property(lambda r: r.block_state_id & 0xF, blockMeta)
|
||||||
|
|
||||||
|
# This alias is retained for backward compatibility.
|
||||||
|
def blockStateId(self, block_state_id):
|
||||||
|
self.block_state_id = block_state_id
|
||||||
|
blockStateId = property(lambda r: r.block_state_id, blockStateId)
|
||||||
|
|
||||||
|
def read(self, file_object):
|
||||||
|
h_position = UnsignedByte.read(file_object)
|
||||||
|
self.x, self.z = h_position >> 4, h_position & 0xF
|
||||||
|
self.y = UnsignedByte.read(file_object)
|
||||||
|
self.block_state_id = VarInt.read(file_object)
|
||||||
|
|
||||||
|
def write(self, packet_buffer):
|
||||||
|
UnsignedByte.send(self.x << 4 | self.z & 0xF, packet_buffer)
|
||||||
|
UnsignedByte.send(self.y, packet_buffer)
|
||||||
|
VarInt.send(self.block_state_id, packet_buffer)
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
self.chunk_x = Integer.read(file_object)
|
self.chunk_x = Integer.read(file_object)
|
||||||
self.chunk_z = Integer.read(file_object)
|
self.chunk_z = Integer.read(file_object)
|
||||||
records_count = VarInt.read(file_object)
|
records_count = VarInt.read(file_object)
|
||||||
record_type = self.BaseRecord.get_subclass(self.context)
|
|
||||||
self.records = []
|
self.records = []
|
||||||
for i in range(records_count):
|
for i in range(records_count):
|
||||||
record = record_type(h_position=UnsignedByte.read(file_object),
|
record = self.Record()
|
||||||
y_coordinate=UnsignedByte.read(file_object),
|
record.read(file_object)
|
||||||
blockData=VarInt.read(file_object))
|
|
||||||
self.records.append(record)
|
self.records.append(record)
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
def write_fields(self, packet_buffer):
|
||||||
raise NotImplementedError
|
Integer.send(self.chunk_x, packet_buffer)
|
||||||
|
Integer.send(self.chunk_z, packet_buffer)
|
||||||
|
VarInt.send(len(self.records), packet_buffer)
|
||||||
|
for record in self.records:
|
||||||
|
record.write(packet_buffer)
|
||||||
|
@ -19,48 +19,84 @@ class CombatEventPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'combat event'
|
packet_name = 'combat event'
|
||||||
|
|
||||||
|
# The abstract type of the 'event' field of this packet.
|
||||||
class EventType(object):
|
class EventType(object):
|
||||||
def read(self, file_object):
|
__slots__ = ()
|
||||||
self._read(file_object)
|
type_from_id_dict = {}
|
||||||
|
|
||||||
def _read(self, file_object):
|
def __init__(self, **kwds):
|
||||||
|
for attr, value in kwds.items():
|
||||||
|
setattr(self, attr, value)
|
||||||
|
|
||||||
|
def __repr__(self, **kwds):
|
||||||
|
return '%s(%s)' % (type(self).__name__, ', '.join(
|
||||||
|
'%s=%r' % (a, getattr(self, a)) for a in self.__slots__))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return type(self) is type(other) and all(
|
||||||
|
getattr(self, a) == getattr(other, a) for a in self.__slots__)
|
||||||
|
|
||||||
|
# Read the fields of the event (not including the ID) from the file.
|
||||||
|
def read(self, file_object):
|
||||||
|
raise NotImplementedError(
|
||||||
|
'This abstract method must be overridden in a subclass.')
|
||||||
|
|
||||||
|
# Write the fields of the event (not including the ID) to the buffer.
|
||||||
|
def write(self, packet_buffer):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'This abstract method must be overridden in a subclass.')
|
'This abstract method must be overridden in a subclass.')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def type_from_id(cls, event_id):
|
def type_from_id(cls, event_id):
|
||||||
subcls = {
|
subcls = cls.type_from_id_dict.get(event_id)
|
||||||
0: CombatEventPacket.EnterCombatEvent,
|
|
||||||
1: CombatEventPacket.EndCombatEvent,
|
|
||||||
2: CombatEventPacket.EntityDeadEvent
|
|
||||||
}.get(event_id)
|
|
||||||
if subcls is None:
|
if subcls is None:
|
||||||
raise ValueError("Unknown combat event ID: %s."
|
raise ValueError('Unknown combat event ID: %s.' % event_id)
|
||||||
% event_id)
|
|
||||||
return subcls
|
return subcls
|
||||||
|
|
||||||
class EnterCombatEvent(EventType):
|
class EnterCombatEvent(EventType):
|
||||||
def _read(self, file_object):
|
__slots__ = ()
|
||||||
|
id = 0
|
||||||
|
|
||||||
|
def read(self, file_object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def write(self, packet_buffer):
|
||||||
|
pass
|
||||||
|
EventType.type_from_id_dict[EnterCombatEvent.id] = EnterCombatEvent
|
||||||
|
|
||||||
class EndCombatEvent(EventType):
|
class EndCombatEvent(EventType):
|
||||||
__slots__ = 'duration', 'entity_id'
|
__slots__ = 'duration', 'entity_id'
|
||||||
|
id = 1
|
||||||
|
|
||||||
def _read(self, file_object):
|
def read(self, file_object):
|
||||||
self.duration = VarInt.read(file_object)
|
self.duration = VarInt.read(file_object)
|
||||||
self.entity_id = Integer.read(file_object)
|
self.entity_id = Integer.read(file_object)
|
||||||
|
|
||||||
|
def write(self, packet_buffer):
|
||||||
|
VarInt.send(self.duration, packet_buffer)
|
||||||
|
Integer.send(self.entity_id, packet_buffer)
|
||||||
|
EventType.type_from_id_dict[EndCombatEvent.id] = EndCombatEvent
|
||||||
|
|
||||||
class EntityDeadEvent(EventType):
|
class EntityDeadEvent(EventType):
|
||||||
__slots__ = 'player_id', 'entity_id', 'message'
|
__slots__ = 'player_id', 'entity_id', 'message'
|
||||||
|
id = 2
|
||||||
|
|
||||||
def _read(self, file_object):
|
def read(self, file_object):
|
||||||
self.player_id = VarInt.read(file_object)
|
self.player_id = VarInt.read(file_object)
|
||||||
self.entity_id = Integer.read(file_object)
|
self.entity_id = Integer.read(file_object)
|
||||||
self.message = String.read(file_object)
|
self.message = String.read(file_object)
|
||||||
|
|
||||||
|
def write(self, packet_buffer):
|
||||||
|
VarInt.send(self.player_id, packet_buffer)
|
||||||
|
Integer.send(self.entity_id, packet_buffer)
|
||||||
|
String.send(self.message, packet_buffer)
|
||||||
|
EventType.type_from_id_dict[EntityDeadEvent.id] = EntityDeadEvent
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
event_id = VarInt.read(file_object)
|
event_id = VarInt.read(file_object)
|
||||||
self.event_type = CombatEventPacket.EventType.type_from_id(event_id)
|
self.event = CombatEventPacket.EventType.type_from_id(event_id)()
|
||||||
|
self.event.read(file_object)
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
def write_fields(self, packet_buffer):
|
||||||
raise NotImplementedError
|
VarInt.send(self.event.id, packet_buffer)
|
||||||
|
self.event.write(packet_buffer)
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
from minecraft.networking.types import Vector, Float, Byte, Integer
|
||||||
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
|
|
||||||
|
class ExplosionPacket(Packet):
|
||||||
|
@staticmethod
|
||||||
|
def get_id(context):
|
||||||
|
return 0x1D if context.protocol_version >= 345 else \
|
||||||
|
0x1C if context.protocol_version >= 332 else \
|
||||||
|
0x1D if context.protocol_version >= 318 else \
|
||||||
|
0x1C if context.protocol_version >= 80 else \
|
||||||
|
0x1B if context.protocol_version >= 67 else \
|
||||||
|
0x27
|
||||||
|
|
||||||
|
packet_name = 'explosion'
|
||||||
|
|
||||||
|
class Record(Vector):
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def position(self):
|
||||||
|
return Vector(self.x, self.y, self.x)
|
||||||
|
|
||||||
|
@position.setter
|
||||||
|
def position(self, new_position):
|
||||||
|
self.x, self.y, self.z = new_position
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_motion(self):
|
||||||
|
return Vector(self.player_motion_x, self.player_motion_y,
|
||||||
|
self.player_motion_z)
|
||||||
|
|
||||||
|
@player_motion.setter
|
||||||
|
def player_motion(self, new_player_motion):
|
||||||
|
self.player_motion_x, self.player_motion_y, self.player_motion_z \
|
||||||
|
= new_player_motion
|
||||||
|
|
||||||
|
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)
|
@ -1,7 +1,4 @@
|
|||||||
from minecraft.networking.packets import (
|
from minecraft.networking.packets import Packet
|
||||||
Packet, PacketBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String
|
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String
|
||||||
)
|
)
|
||||||
@ -116,10 +113,7 @@ class MapPacket(Packet):
|
|||||||
map_set.maps_by_id[self.map_id] = map
|
map_set.maps_by_id[self.map_id] = map
|
||||||
self.apply_to_map(map)
|
self.apply_to_map(map)
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
def write_fields(self, packet_buffer):
|
||||||
packet_buffer = PacketBuffer()
|
|
||||||
VarInt.send(self.id, packet_buffer)
|
|
||||||
|
|
||||||
VarInt.send(self.map_id, packet_buffer)
|
VarInt.send(self.map_id, packet_buffer)
|
||||||
Byte.send(self.scale, packet_buffer)
|
Byte.send(self.scale, packet_buffer)
|
||||||
if self.context.protocol_version >= 107:
|
if self.context.protocol_version >= 107:
|
||||||
@ -140,8 +134,6 @@ class MapPacket(Packet):
|
|||||||
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)
|
||||||
|
|
||||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%sMapPacket(%s)' % (
|
return '%sMapPacket(%s)' % (
|
||||||
('0x%02X ' % self.id) if self.id is not None else '',
|
('0x%02X ' % self.id) if self.id is not None else '',
|
||||||
|
@ -156,5 +156,5 @@ class PlayerListItemPacket(Packet):
|
|||||||
for action in self.actions:
|
for action in self.actions:
|
||||||
action.apply(player_list)
|
action.apply(player_list)
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
def write_fields(self, packet_buffer):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -72,5 +72,5 @@ class SpawnObjectPacket(Packet):
|
|||||||
self.velocity_y = Short.read(file_object)
|
self.velocity_y = Short.read(file_object)
|
||||||
self.velocity_z = Short.read(file_object)
|
self.velocity_z = Short.read(file_object)
|
||||||
|
|
||||||
def write(self, socket, compression_threshold=None):
|
def write_fields(self, packet_buffer):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -25,9 +25,9 @@ class Packet(object):
|
|||||||
# 2. Override `get_definition' in a subclass and return the correct
|
# 2. Override `get_definition' in a subclass and return the correct
|
||||||
# definition for the given ConnectionContext. This may be necessary
|
# definition for the given ConnectionContext. This may be necessary
|
||||||
# if the layout has changed across protocol versions, for example; or
|
# if the layout has changed across protocol versions, for example; or
|
||||||
# 3. Override the methods `read' and/or `write' in a subclass. This may be
|
# 3. Override the methods `read' and/or `write_fields' in a subclass.
|
||||||
# necessary if the packet layout cannot be described as a list of
|
# This may be necessary if the packet layout cannot be described as a
|
||||||
# fields.
|
# simple list of fields.
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_definition(cls, context):
|
def get_definition(cls, context):
|
||||||
return cls.definition
|
return cls.definition
|
||||||
@ -95,13 +95,17 @@ class Packet(object):
|
|||||||
# write packet's id right off the bat in the header
|
# write packet's id right off the bat in the header
|
||||||
VarInt.send(self.id, packet_buffer)
|
VarInt.send(self.id, packet_buffer)
|
||||||
# write every individual field
|
# write every individual field
|
||||||
|
self.write_fields(packet_buffer)
|
||||||
|
self._write_buffer(socket, packet_buffer, compression_threshold)
|
||||||
|
|
||||||
|
def write_fields(self, packet_buffer):
|
||||||
|
# Write the fields comprising the body of the packet (excluding the
|
||||||
|
# length, packet ID, compression and encryption) into a PacketBuffer.
|
||||||
for field in self.definition:
|
for field in self.definition:
|
||||||
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(data, packet_buffer)
|
data_type.send(data, packet_buffer)
|
||||||
|
|
||||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
str = type(self).__name__
|
str = type(self).__name__
|
||||||
if self.id is not None:
|
if self.id is not None:
|
||||||
|
@ -7,6 +7,11 @@ import uuid
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: subclasses of 'Vector' should have '__slots__ = ()' to avoid the
|
||||||
|
# creation of a '__dict__' attribute, which would waste space.
|
||||||
|
Vector = namedtuple('Vector', ('x', 'y', 'z'))
|
||||||
|
|
||||||
|
|
||||||
class Type(object):
|
class Type(object):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@ -241,7 +246,7 @@ class UUID(Type):
|
|||||||
socket.send(uuid.UUID(value).bytes)
|
socket.send(uuid.UUID(value).bytes)
|
||||||
|
|
||||||
|
|
||||||
class Position(Type, namedtuple('Position', ('x', 'y', 'z'))):
|
class Position(Type, Vector):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from minecraft import SUPPORTED_PROTOCOL_VERSIONS
|
||||||
|
from minecraft.networking.connection import ConnectionContext
|
||||||
from minecraft.networking import packets
|
from minecraft.networking import packets
|
||||||
from minecraft.networking import types
|
from minecraft.networking import types
|
||||||
from minecraft.networking.packets import clientbound
|
from minecraft.networking.packets import clientbound
|
||||||
@ -91,3 +93,11 @@ class ClassMemberAliasesTest(unittest.TestCase):
|
|||||||
types.AbsoluteHand.LEFT)
|
types.AbsoluteHand.LEFT)
|
||||||
self.assertEqual(serverbound.play.ClientSettingsPacket.Hand.RIGHT,
|
self.assertEqual(serverbound.play.ClientSettingsPacket.Hand.RIGHT,
|
||||||
types.AbsoluteHand.RIGHT)
|
types.AbsoluteHand.RIGHT)
|
||||||
|
|
||||||
|
def test_block_change_packet(self):
|
||||||
|
context = ConnectionContext()
|
||||||
|
context.protocol_version = SUPPORTED_PROTOCOL_VERSIONS[-1]
|
||||||
|
bi, bm = 358, 9
|
||||||
|
packet = clientbound.play.BlockChangePacket(blockId=bi, blockMeta=bm)
|
||||||
|
self.assertEqual((packet.blockId, packet.blockMeta), (bi, bm))
|
||||||
|
self.assertEqual(packet.blockStateId, packet.block_state_id)
|
||||||
|
@ -6,9 +6,11 @@ from random import choice
|
|||||||
|
|
||||||
from minecraft import SUPPORTED_PROTOCOL_VERSIONS
|
from minecraft import SUPPORTED_PROTOCOL_VERSIONS
|
||||||
from minecraft.networking.connection import ConnectionContext
|
from minecraft.networking.connection import ConnectionContext
|
||||||
from minecraft.networking.types import VarInt, Enum, BitFieldEnum
|
from minecraft.networking.types import VarInt, Enum, BitFieldEnum, Vector
|
||||||
from minecraft.networking.packets import (
|
from minecraft.networking.packets import (
|
||||||
Packet, PacketBuffer, PacketListener, KeepAlivePacket, serverbound)
|
Packet, PacketBuffer, PacketListener, KeepAlivePacket, serverbound,
|
||||||
|
clientbound,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PacketBufferTest(unittest.TestCase):
|
class PacketBufferTest(unittest.TestCase):
|
||||||
@ -35,7 +37,7 @@ class PacketBufferTest(unittest.TestCase):
|
|||||||
self.assertEqual(packet_buffer.get_writable(), message)
|
self.assertEqual(packet_buffer.get_writable(), message)
|
||||||
|
|
||||||
|
|
||||||
class PacketSerializatonTest(unittest.TestCase):
|
class PacketSerializationTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_packet(self):
|
def test_packet(self):
|
||||||
for protocol_version in SUPPORTED_PROTOCOL_VERSIONS:
|
for protocol_version in SUPPORTED_PROTOCOL_VERSIONS:
|
||||||
@ -173,3 +175,58 @@ class BitFieldEnumTest(unittest.TestCase):
|
|||||||
list(map(Example2.name_from_value, range(9))),
|
list(map(Example2.name_from_value, range(9))),
|
||||||
['0', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
|
['0', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
|
||||||
'ONE|FOUR', 'TWO|FOUR', 'ONE|TWO|FOUR', None])
|
'ONE|FOUR', 'TWO|FOUR', 'ONE|TWO|FOUR', None])
|
||||||
|
|
||||||
|
|
||||||
|
class TestReadWritePackets(unittest.TestCase):
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.context = ConnectionContext()
|
||||||
|
self.context.protocol_version = SUPPORTED_PROTOCOL_VERSIONS[-1]
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
del self.context
|
||||||
|
|
||||||
|
def test_explosion_packet(self):
|
||||||
|
Record = clientbound.play.ExplosionPacket.Record
|
||||||
|
packet = clientbound.play.ExplosionPacket(
|
||||||
|
position=Vector(787, -37, 0), radius=15,
|
||||||
|
records=[Record(-14, -116, -5), Record(-77, 34, -36),
|
||||||
|
Record(-35, -127, 95), Record(11, 113, -8)],
|
||||||
|
player_motion=Vector(4, 5, 0))
|
||||||
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
|
def test_combat_event_packet(self):
|
||||||
|
packet = clientbound.play.CombatEventPacket()
|
||||||
|
for event in (
|
||||||
|
packet.EnterCombatEvent(),
|
||||||
|
packet.EndCombatEvent(duration=415, entity_id=91063502),
|
||||||
|
packet.EntityDeadEvent(player_id=178, entity_id=36, message='RIP'),
|
||||||
|
):
|
||||||
|
packet.event = event
|
||||||
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
|
def test_multi_block_change_packet(self):
|
||||||
|
Record = clientbound.play.MultiBlockChangePacket.Record
|
||||||
|
packet = clientbound.play.MultiBlockChangePacket(
|
||||||
|
chunk_x=167, chunk_z=15, records=[
|
||||||
|
Record(x=1, y=2, z=3, blockId=56, blockMeta=13),
|
||||||
|
Record(position=Vector(1, 2, 3), block_state_id=909),
|
||||||
|
Record(position=(1, 2, 3), blockStateId=909)])
|
||||||
|
self.assertEqual(packet.records[0], packet.records[1])
|
||||||
|
self.assertEqual(packet.records[1], packet.records[2])
|
||||||
|
self._test_read_write_packet(packet)
|
||||||
|
|
||||||
|
def _test_read_write_packet(self, packet_in):
|
||||||
|
packet_in.context = self.context
|
||||||
|
packet_buffer = PacketBuffer()
|
||||||
|
packet_in.write(packet_buffer)
|
||||||
|
packet_buffer.reset_cursor()
|
||||||
|
VarInt.read(packet_buffer)
|
||||||
|
packet_id = VarInt.read(packet_buffer)
|
||||||
|
self.assertEqual(packet_id, packet_in.id)
|
||||||
|
|
||||||
|
packet_out = type(packet_in)(context=self.context)
|
||||||
|
packet_out.read(packet_buffer)
|
||||||
|
self.assertIs(type(packet_in), type(packet_out))
|
||||||
|
self.assertEqual(packet_in.__dict__, packet_out.__dict__)
|
||||||
|
Loading…
Reference in New Issue
Block a user