mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-22 02:08:56 +01:00
Merge pull request #125 from SirGhostal/new-packets
* Add Angle type for byte-encoded rotation angles. * Fix: SpawnPlayerPacket and SpawnObjectPacket use wrong type for pitch/yaw. * Add FacePlayerPacket and EntityLookPacket.
This commit is contained in:
commit
b83b33f8df
@ -3,8 +3,8 @@ from minecraft.networking.packets import (
|
||||
)
|
||||
|
||||
from minecraft.networking.types import (
|
||||
Integer, FixedPointInteger, UnsignedByte, Byte, Boolean, UUID, Short,
|
||||
VarInt, Double, Float, String, Enum, Difficulty, Dimension, GameMode,
|
||||
Integer, FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID,
|
||||
Short, VarInt, Double, Float, String, Enum, Difficulty, Dimension, GameMode
|
||||
)
|
||||
|
||||
from .combat_event_packet import CombatEventPacket
|
||||
@ -15,6 +15,7 @@ from .spawn_object_packet import SpawnObjectPacket
|
||||
from .block_change_packet import BlockChangePacket, MultiBlockChangePacket
|
||||
from .explosion_packet import ExplosionPacket
|
||||
from .sound_effect_packet import SoundEffectPacket
|
||||
from .face_player_packet import FacePlayerPacket
|
||||
|
||||
|
||||
# Formerly known as state_playing_clientbound.
|
||||
@ -39,6 +40,7 @@ def get_packets(context):
|
||||
RespawnPacket,
|
||||
PluginMessagePacket,
|
||||
PlayerListHeaderAndFooterPacket,
|
||||
EntityLookPacket
|
||||
}
|
||||
if context.protocol_version <= 47:
|
||||
packets |= {
|
||||
@ -48,6 +50,10 @@ def get_packets(context):
|
||||
packets |= {
|
||||
SoundEffectPacket,
|
||||
}
|
||||
if context.protocol_version >= 352:
|
||||
packets |= {
|
||||
FacePlayerPacket
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
@ -173,8 +179,8 @@ class SpawnPlayerPacket(Packet):
|
||||
else {'y': FixedPointInteger},
|
||||
{'z': Double} if context.protocol_version >= 100
|
||||
else {'z': FixedPointInteger},
|
||||
{'yaw': Float},
|
||||
{'pitch': Float},
|
||||
{'yaw': Angle},
|
||||
{'pitch': Angle},
|
||||
# TODO: read entity metadata
|
||||
{'current_item': Short} if context.protocol_version <= 49 else {}
|
||||
])
|
||||
@ -291,3 +297,22 @@ class PlayerListHeaderAndFooterPacket(Packet):
|
||||
definition = [
|
||||
{'header': String},
|
||||
{'footer': String}]
|
||||
|
||||
|
||||
class EntityLookPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x2A if context.protocol_version >= 389 else \
|
||||
0x29 if context.protocol_version >= 345 else \
|
||||
0x28 if context.protocol_version >= 318 else \
|
||||
0x27 if context.protocol_version >= 94 else \
|
||||
0x28 if context.protocol_version >= 70 else \
|
||||
0x16
|
||||
|
||||
packet_name = 'entity look'
|
||||
definition = [
|
||||
{'entity_id': VarInt},
|
||||
{'yaw': Angle},
|
||||
{'pitch': Angle},
|
||||
{'on_ground': Boolean}
|
||||
]
|
||||
|
@ -0,0 +1,69 @@
|
||||
from minecraft.networking.types import (
|
||||
VarInt, Double, Boolean, OriginPoint, Vector, multi_attribute_alias
|
||||
)
|
||||
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
|
||||
class FacePlayerPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x34 if context.protocol_version >= 471 else \
|
||||
0x32 if context.protocol_version >= 451 else \
|
||||
0x31 if context.protocol_version >= 389 else \
|
||||
0x30
|
||||
|
||||
packet_name = 'face player'
|
||||
|
||||
# Access the 'x', 'y', 'z' fields as a Vector.
|
||||
target = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||
|
||||
def read(self, file_object):
|
||||
if self.context.protocol_version >= 353:
|
||||
self.origin = VarInt.read(file_object)
|
||||
self.x = Double.read(file_object)
|
||||
self.y = Double.read(file_object)
|
||||
self.z = Double.read(file_object)
|
||||
is_entity = Boolean.read(file_object)
|
||||
if is_entity:
|
||||
# If the entity given by entity ID cannot be found,
|
||||
# this packet should be treated as if is_entity was false.
|
||||
self.entity_id = VarInt.read(file_object)
|
||||
self.entity_origin = VarInt.read(file_object)
|
||||
else:
|
||||
self.entity_id = None
|
||||
|
||||
else: # Protocol version 352
|
||||
is_entity = Boolean.read(file_object)
|
||||
self.entity_id = VarInt.read(file_object) if is_entity else None
|
||||
if not is_entity:
|
||||
self.x = Double.read(file_object)
|
||||
self.y = Double.read(file_object)
|
||||
self.z = Double.read(file_object)
|
||||
|
||||
def write_fields(self, packet_buffer):
|
||||
if self.context.protocol_version >= 353:
|
||||
VarInt.send(self.origin, packet_buffer)
|
||||
Double.send(self.x, packet_buffer)
|
||||
Double.send(self.y, packet_buffer)
|
||||
Double.send(self.z, packet_buffer)
|
||||
if self.entity_id is not None:
|
||||
Boolean.send(True, packet_buffer)
|
||||
VarInt.send(self.entity_id, packet_buffer)
|
||||
VarInt.send(self.entity_origin, packet_buffer)
|
||||
else:
|
||||
Boolean.send(False, packet_buffer)
|
||||
|
||||
else: # Protocol version 352
|
||||
if self.entity_id is not None:
|
||||
Boolean.send(True, packet_buffer)
|
||||
VarInt.send(self.entity_id, packet_buffer)
|
||||
else:
|
||||
Boolean.send(False, packet_buffer)
|
||||
Double.send(self.x, packet_buffer)
|
||||
Double.send(self.y, packet_buffer)
|
||||
Double.send(self.z, packet_buffer)
|
||||
|
||||
# These aliases declare the Enum type corresponding to each field:
|
||||
Origin = OriginPoint
|
||||
EntityOrigin = OriginPoint
|
@ -2,7 +2,7 @@ from minecraft.networking.packets import Packet
|
||||
from minecraft.networking.types.utility import descriptor
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt, UUID, Byte, Double, Integer, UnsignedByte, Short, Enum, Vector,
|
||||
VarInt, UUID, Byte, Double, Integer, Angle, Short, Enum, Vector,
|
||||
PositionAndLook, attribute_alias, multi_attribute_alias,
|
||||
)
|
||||
|
||||
@ -103,7 +103,7 @@ class SpawnObjectPacket(Packet):
|
||||
for attr in 'x', 'y', 'z':
|
||||
setattr(self, attr, xyz_type.read(file_object))
|
||||
for attr in 'pitch', 'yaw':
|
||||
setattr(self, attr, UnsignedByte.read(file_object))
|
||||
setattr(self, attr, Angle.read(file_object))
|
||||
|
||||
self.data = Integer.read(file_object)
|
||||
if self.context.protocol_version >= 49 or self.data > 0:
|
||||
@ -125,7 +125,7 @@ class SpawnObjectPacket(Packet):
|
||||
for coord in self.x, self.y, self.z:
|
||||
xyz_type.send(coord, packet_buffer)
|
||||
for coord in self.pitch, self.yaw:
|
||||
UnsignedByte.send(coord, packet_buffer)
|
||||
Angle.send(coord, packet_buffer)
|
||||
|
||||
Integer.send(self.data, packet_buffer)
|
||||
if self.context.protocol_version >= 49 or self.data > 0:
|
||||
|
@ -11,9 +11,10 @@ from .utility import Vector
|
||||
|
||||
__all__ = (
|
||||
'Type', 'Boolean', 'UnsignedByte', 'Byte', 'Short', 'UnsignedShort',
|
||||
'Integer', 'FixedPointInteger', 'VarInt', 'Long', 'UnsignedLong', 'Float',
|
||||
'Double', 'ShortPrefixedByteArray', 'VarIntPrefixedByteArray',
|
||||
'TrailingByteArray', 'String', 'UUID', 'Position',
|
||||
'Integer', 'FixedPointInteger', 'Angle', 'VarInt', 'Long',
|
||||
'UnsignedLong', 'Float', 'Double', 'ShortPrefixedByteArray',
|
||||
'VarIntPrefixedByteArray', 'TrailingByteArray', 'String', 'UUID',
|
||||
'Position',
|
||||
)
|
||||
|
||||
|
||||
@ -117,6 +118,18 @@ class FixedPointInteger(Type):
|
||||
Integer.send(int(value * 32), socket)
|
||||
|
||||
|
||||
class Angle(Type):
|
||||
@staticmethod
|
||||
def read(file_object):
|
||||
# Linearly transform angle in steps of 1/256 into steps of 1/360
|
||||
return 360 * UnsignedByte.read(file_object) / 256
|
||||
|
||||
@staticmethod
|
||||
def send(value, socket):
|
||||
# Normalize angle between 0 and 255 and convert to int.
|
||||
UnsignedByte.send(round(256 * ((value % 360) / 360)), socket)
|
||||
|
||||
|
||||
class VarInt(Type):
|
||||
@staticmethod
|
||||
def read(file_object):
|
||||
|
@ -12,7 +12,7 @@ from .utility import Vector
|
||||
|
||||
__all__ = (
|
||||
'Enum', 'BitFieldEnum', 'AbsoluteHand', 'RelativeHand', 'BlockFace',
|
||||
'Difficulty', 'Dimension', 'GameMode'
|
||||
'Difficulty', 'Dimension', 'GameMode', 'OriginPoint'
|
||||
)
|
||||
|
||||
|
||||
@ -106,3 +106,10 @@ class GameMode(Enum):
|
||||
CREATIVE = 1
|
||||
ADVENTURE = 2
|
||||
SPECTATOR = 3
|
||||
|
||||
|
||||
# Currently designates an entity's feet or eyes.
|
||||
# Used in the Face Player Packet
|
||||
class OriginPoint(Enum):
|
||||
FEET = 0
|
||||
EYES = 1
|
||||
|
@ -9,7 +9,7 @@ from random import choice
|
||||
from minecraft import SUPPORTED_PROTOCOL_VERSIONS, RELEASE_PROTOCOL_VERSIONS
|
||||
from minecraft.networking.connection import ConnectionContext
|
||||
from minecraft.networking.types import (
|
||||
VarInt, Enum, Vector, PositionAndLook
|
||||
VarInt, Enum, Vector, PositionAndLook, OriginPoint,
|
||||
)
|
||||
from minecraft.networking.packets import (
|
||||
Packet, PacketBuffer, PacketListener, KeepAlivePacket, serverbound,
|
||||
@ -199,9 +199,9 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
'type_id', context)
|
||||
|
||||
pos_look = PositionAndLook(
|
||||
position=(Vector(68.0, 38.0, 76.0) if context.protocol_version
|
||||
>= 100 else Vector(68, 38, 76)),
|
||||
yaw=16, pitch=23)
|
||||
position=(Vector(68.0, 38.0, 76.0) if protocol_version >= 100
|
||||
else Vector(68, 38, 76)),
|
||||
yaw=263.494, pitch=180)
|
||||
velocity = Vector(21, 55, 41)
|
||||
entity_id, type_name, type_id = 49846, 'EGG', EntityType.EGG
|
||||
|
||||
@ -212,7 +212,7 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
velocity_x=velocity.x, velocity_y=velocity.y,
|
||||
velocity_z=velocity.z,
|
||||
entity_id=entity_id, type_id=type_id, data=1)
|
||||
if context.protocol_version >= 49:
|
||||
if protocol_version >= 49:
|
||||
object_uuid = 'd9568851-85bc-4a10-8d6a-261d130626fa'
|
||||
packet.object_uuid = object_uuid
|
||||
self.assertEqual(packet.objectUUID, object_uuid)
|
||||
@ -225,7 +225,7 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
context=context, position_and_look=pos_look,
|
||||
velocity=velocity, type=type_name,
|
||||
entity_id=entity_id, data=1)
|
||||
if context.protocol_version >= 49:
|
||||
if protocol_version >= 49:
|
||||
packet2.object_uuid = object_uuid
|
||||
self.assertEqual(packet.__dict__, packet2.__dict__)
|
||||
|
||||
@ -233,10 +233,12 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
self.assertEqual(packet.position, packet2.position)
|
||||
|
||||
packet2.data = 0
|
||||
if context.protocol_version < 49:
|
||||
if protocol_version < 49:
|
||||
del packet2.velocity
|
||||
self._test_read_write_packet(packet, context)
|
||||
self._test_read_write_packet(packet2, context)
|
||||
self._test_read_write_packet(packet, context,
|
||||
yaw=360/256, pitch=360/256)
|
||||
self._test_read_write_packet(packet2, context,
|
||||
yaw=360/256, pitch=360/256)
|
||||
|
||||
def test_sound_effect_packet(self):
|
||||
for protocol_version in TEST_VERSIONS:
|
||||
@ -245,7 +247,7 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
packet = clientbound.play.SoundEffectPacket(
|
||||
sound_id=545, effect_position=Vector(0.125, 300.0, 50.5),
|
||||
volume=0.75)
|
||||
if context.protocol_version >= 201:
|
||||
if protocol_version >= 201:
|
||||
packet.pitch = struct.unpack('f', struct.pack('f', 1.5))[0]
|
||||
else:
|
||||
packet.pitch = int(1.5 / 63.5) * 63.5
|
||||
@ -254,7 +256,30 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
clientbound.play.SoundEffectPacket.SoundCategory.NEUTRAL
|
||||
self._test_read_write_packet(packet, context)
|
||||
|
||||
def _test_read_write_packet(self, packet_in, context=None):
|
||||
def test_face_player_packet(self):
|
||||
for protocol_version in TEST_VERSIONS:
|
||||
context = ConnectionContext(protocol_version=protocol_version)
|
||||
|
||||
packet = clientbound.play.FacePlayerPacket()
|
||||
packet.target = 1.0, -2.0, 3.5
|
||||
packet.entity_id = None
|
||||
if protocol_version >= 353:
|
||||
packet.origin = OriginPoint.EYES
|
||||
self._test_read_write_packet(packet, context)
|
||||
|
||||
packet.entity_id = 123
|
||||
if protocol_version >= 353:
|
||||
packet.entity_origin = OriginPoint.FEET
|
||||
else:
|
||||
del packet.target
|
||||
self._test_read_write_packet(packet, context)
|
||||
|
||||
def _test_read_write_packet(self, packet_in, context=None, **kwargs):
|
||||
"""
|
||||
If kwargs are specified, the key will be tested against the
|
||||
respective delta value. Useful for testing FixedPointNumbers
|
||||
where there is precision lost in the resulting value.
|
||||
"""
|
||||
if context is None:
|
||||
for protocol_version in TEST_VERSIONS:
|
||||
logging.debug('protocol_version = %r' % protocol_version)
|
||||
@ -272,4 +297,12 @@ class TestReadWritePackets(unittest.TestCase):
|
||||
packet_out = type(packet_in)(context=context)
|
||||
packet_out.read(packet_buffer)
|
||||
self.assertIs(type(packet_in), type(packet_out))
|
||||
|
||||
for packet_attr, precision in kwargs.items():
|
||||
packet_attribute_in = packet_in.__dict__.pop(packet_attr)
|
||||
packet_attribute_out = packet_out.__dict__.pop(packet_attr)
|
||||
self.assertAlmostEqual(packet_attribute_in,
|
||||
packet_attribute_out,
|
||||
delta=precision)
|
||||
|
||||
self.assertEqual(packet_in.__dict__, packet_out.__dict__)
|
||||
|
@ -3,7 +3,7 @@
|
||||
import unittest
|
||||
from minecraft.networking.types import (
|
||||
Type, Boolean, UnsignedByte, Byte, Short, UnsignedShort,
|
||||
Integer, FixedPointInteger, VarInt, Long, Float, Double,
|
||||
Integer, FixedPointInteger, Angle, VarInt, Long, Float, Double,
|
||||
ShortPrefixedByteArray, VarIntPrefixedByteArray, UUID,
|
||||
String as StringType, Position, TrailingByteArray, UnsignedLong,
|
||||
)
|
||||
@ -25,6 +25,7 @@ TEST_DATA = {
|
||||
UnsignedLong: [0, 400],
|
||||
Integer: [-1000, 1000],
|
||||
FixedPointInteger: [float(-13098.3435), float(-0.83), float(1000)],
|
||||
Angle: [0, 360.0, 720, 47.12947238973, -108.7],
|
||||
VarInt: [1, 250, 50000, 10000000],
|
||||
Long: [50000000],
|
||||
Float: [21.000301],
|
||||
@ -58,8 +59,12 @@ class SerializationTest(unittest.TestCase):
|
||||
if data_type is FixedPointInteger:
|
||||
self.assertAlmostEqual(
|
||||
test_data, deserialized, delta=1.0/32.0)
|
||||
elif data_type is Angle:
|
||||
self.assertAlmostEqual(test_data % 360,
|
||||
deserialized,
|
||||
delta=360/256)
|
||||
elif data_type is Float or data_type is Double:
|
||||
self.assertAlmostEquals(test_data, deserialized, 3)
|
||||
self.assertAlmostEqual(test_data, deserialized, 3)
|
||||
else:
|
||||
self.assertEqual(test_data, deserialized)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user