mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2025-01-24 00:21:31 +01:00
969419da3f
After 1.16.3, Mojang started publishing snapshot, pre-release and release candidate versions of Minecraft with protocol version numbers of the form `(1 << 30) | n' where 'n' is a small non-negative integer increasing with each such version; the release versions continued to use the old format. For example, these are the last 8 published Minecraft versions as of this commit: release 1.16.3 uses protocol version 753 pre-release 1.16.4-pre1 uses protocol version 1073741825 == (1 << 30) | 1 pre-release 1.16.4-pre2 uses protocol version 1073741826 == (1 << 30) | 2 release candidate 1.16.4-rc1 uses protocol version 1073741827 == (1 << 30) | 3 release 1.16.4 uses protocol version 754 snapshot 20w45a uses protocol version 1073741829 == (1 << 30) | 5 snapshot 20w46a uses protocol version 1073741830 == (1 << 30) | 6 snapshot 20w48a uses protocol version 1073741831 == (1 << 30) | 7 This means that protocol versions no longer increase monotonically with respect to publication history, a property that was assumed to hold in much of pyCraft's code relating to support of multiple protocol versions. This commit rectifies the issue by replacing any comparison of protocol versions by their numerical value with a comparison based on their publication time. Newly defined is the dictionary `minecraft.PROTOCOL_VERSION_INDICES', which maps each known protocol version to its index in the protocol chronology. As such, the bound method `minecraft.PROTOCOL_VERSION_INDICES.get` can be used as a key function for the built-in `sorted`, `min` and `max` functions to collate protocol versions chronologically. Two utility functions are provided for direct comparison of protocol versions: `minecraft.utility.protocol_earlier` and `minecraft.utility.protocol_earlier_eq`. Additionally, four methods are added to the `ConnectionContext` type to ease the most common cases where the protocol of a given context must be compared to a given version number: `minecraft.connection.ConnectionContext.protocol_earlier`, `minecraft.connection.ConnectionContext.protocol_earlier_eq`, `minecraft.connection.ConnectionContext.protocol_later` and `minecraft.connection.ConnectionContext.protocol_later_eq`.
360 lines
13 KiB
Python
360 lines
13 KiB
Python
from minecraft.networking.packets import (
|
|
Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket
|
|
)
|
|
|
|
from minecraft.networking.types import (
|
|
FixedPoint, Integer, Angle, UnsignedByte, Byte, Boolean, UUID, Short,
|
|
VarInt, Double, Float, String, Enum, Difficulty, Long, Vector, Direction,
|
|
PositionAndLook, multi_attribute_alias, attribute_transform,
|
|
)
|
|
|
|
from .combat_event_packet import CombatEventPacket
|
|
from .map_packet import MapPacket
|
|
from .player_list_item_packet import PlayerListItemPacket
|
|
from .player_position_and_look_packet import PlayerPositionAndLookPacket
|
|
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
|
|
from .join_game_and_respawn_packets import JoinGamePacket, RespawnPacket
|
|
|
|
|
|
# Formerly known as state_playing_clientbound.
|
|
def get_packets(context):
|
|
packets = {
|
|
KeepAlivePacket,
|
|
JoinGamePacket,
|
|
ServerDifficultyPacket,
|
|
ChatMessagePacket,
|
|
PlayerPositionAndLookPacket,
|
|
MapPacket,
|
|
PlayerListItemPacket,
|
|
DisconnectPacket,
|
|
SpawnPlayerPacket,
|
|
EntityVelocityPacket,
|
|
EntityPositionDeltaPacket,
|
|
TimeUpdatePacket,
|
|
UpdateHealthPacket,
|
|
CombatEventPacket,
|
|
ExplosionPacket,
|
|
SpawnObjectPacket,
|
|
BlockChangePacket,
|
|
MultiBlockChangePacket,
|
|
RespawnPacket,
|
|
PluginMessagePacket,
|
|
PlayerListHeaderAndFooterPacket,
|
|
EntityLookPacket
|
|
}
|
|
if context.protocol_earlier_eq(47):
|
|
packets |= {
|
|
SetCompressionPacket,
|
|
}
|
|
if context.protocol_later_eq(94):
|
|
packets |= {
|
|
SoundEffectPacket,
|
|
}
|
|
if context.protocol_later_eq(352):
|
|
packets |= {
|
|
FacePlayerPacket
|
|
}
|
|
return packets
|
|
|
|
|
|
class KeepAlivePacket(AbstractKeepAlivePacket):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x1F if context.protocol_later_eq(741) else \
|
|
0x20 if context.protocol_later_eq(721) else \
|
|
0x21 if context.protocol_later_eq(550) else \
|
|
0x20 if context.protocol_later_eq(471) else \
|
|
0x21 if context.protocol_later_eq(389) else \
|
|
0x20 if context.protocol_later_eq(345) else \
|
|
0x1F if context.protocol_later_eq(332) else \
|
|
0x20 if context.protocol_later_eq(318) else \
|
|
0x1F if context.protocol_later_eq(107) else \
|
|
0x00
|
|
|
|
|
|
class ServerDifficultyPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x0D if context.protocol_later_eq(721) else \
|
|
0x0E if context.protocol_later_eq(550) else \
|
|
0x0D if context.protocol_later_eq(332) else \
|
|
0x0E if context.protocol_later_eq(318) else \
|
|
0x0D if context.protocol_later_eq(70) else \
|
|
0x41
|
|
|
|
packet_name = 'server difficulty'
|
|
get_definition = staticmethod(lambda context: [
|
|
{'difficulty': UnsignedByte},
|
|
{'is_locked': Boolean} if context.protocol_later_eq(464) else {},
|
|
])
|
|
|
|
# These aliases declare the Enum type corresponding to each field:
|
|
Difficulty = Difficulty
|
|
|
|
|
|
class ChatMessagePacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x0E if context.protocol_later_eq(721) else \
|
|
0x0F if context.protocol_later_eq(550) else \
|
|
0x0E if context.protocol_later_eq(343) else \
|
|
0x0F if context.protocol_later_eq(332) else \
|
|
0x10 if context.protocol_later_eq(317) else \
|
|
0x0F if context.protocol_later_eq(107) else \
|
|
0x02
|
|
|
|
packet_name = "chat message"
|
|
get_definition = staticmethod(lambda context: [
|
|
{'json_data': String},
|
|
{'position': Byte},
|
|
{'sender': UUID} if context.protocol_later_eq(718) else {},
|
|
])
|
|
|
|
class Position(Enum):
|
|
CHAT = 0 # A player-initiated chat message.
|
|
SYSTEM = 1 # The result of running a command.
|
|
GAME_INFO = 2 # Displayed above the hotbar in vanilla clients.
|
|
|
|
|
|
class DisconnectPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x19 if context.protocol_later_eq(741) else \
|
|
0x1A if context.protocol_later_eq(721) else \
|
|
0x1B if context.protocol_later_eq(550) else \
|
|
0x1A if context.protocol_later_eq(471) else \
|
|
0x1B if context.protocol_later_eq(345) else \
|
|
0x1A if context.protocol_later_eq(332) else \
|
|
0x1B if context.protocol_later_eq(318) else \
|
|
0x1A if context.protocol_later_eq(107) else \
|
|
0x40
|
|
|
|
packet_name = "disconnect"
|
|
|
|
definition = [
|
|
{'json_data': String}]
|
|
|
|
|
|
class SetCompressionPacket(Packet):
|
|
# Note: removed between protocol versions 47 and 107.
|
|
id = 0x46
|
|
packet_name = "set compression"
|
|
definition = [
|
|
{'threshold': VarInt}]
|
|
|
|
|
|
class SpawnPlayerPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x04 if context.protocol_later_eq(721) else \
|
|
0x05 if context.protocol_later_eq(67) else \
|
|
0x0C
|
|
|
|
packet_name = 'spawn player'
|
|
get_definition = staticmethod(lambda context: [
|
|
{'entity_id': VarInt},
|
|
{'player_UUID': UUID},
|
|
{'x': Double} if context.protocol_later_eq(100)
|
|
else {'x': FixedPoint(Integer)},
|
|
{'y': Double} if context.protocol_later_eq(100)
|
|
else {'y': FixedPoint(Integer)},
|
|
{'z': Double} if context.protocol_later_eq(100)
|
|
else {'z': FixedPoint(Integer)},
|
|
{'yaw': Angle},
|
|
{'pitch': Angle},
|
|
{'current_item': Short} if context.protocol_earlier_eq(49) else {},
|
|
# TODO: read entity metadata (protocol < 550)
|
|
])
|
|
|
|
# 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):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x46 if context.protocol_later_eq(721) else \
|
|
0x47 if context.protocol_later_eq(707) else \
|
|
0x46 if context.protocol_later_eq(550) else \
|
|
0x45 if context.protocol_later_eq(471) else \
|
|
0x41 if context.protocol_later_eq(461) else \
|
|
0x42 if context.protocol_later_eq(451) else \
|
|
0x41 if context.protocol_later_eq(389) else \
|
|
0x40 if context.protocol_later_eq(352) else \
|
|
0x3F if context.protocol_later_eq(345) else \
|
|
0x3E if context.protocol_later_eq(336) else \
|
|
0x3D if context.protocol_later_eq(332) else \
|
|
0x3B if context.protocol_later_eq(86) else \
|
|
0x3C if context.protocol_later_eq(77) else \
|
|
0x3B if context.protocol_later_eq(67) else \
|
|
0x12
|
|
|
|
packet_name = 'entity velocity'
|
|
get_definition = staticmethod(lambda context: [
|
|
{'entity_id': VarInt},
|
|
{'velocity_x': Short},
|
|
{'velocity_y': Short},
|
|
{'velocity_z': Short}
|
|
])
|
|
|
|
|
|
class EntityPositionDeltaPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x27 if context.protocol_later_eq(741) else \
|
|
0x28 if context.protocol_later_eq(721) else \
|
|
0x29 if context.protocol_later_eq(550) else \
|
|
0x28 if context.protocol_later_eq(389) else \
|
|
0x27 if context.protocol_later_eq(345) else \
|
|
0x26 if context.protocol_later_eq(318) else \
|
|
0x25 if context.protocol_later_eq(94) else \
|
|
0x26 if context.protocol_later_eq(70) else \
|
|
0x15
|
|
|
|
packet_name = "entity position delta"
|
|
|
|
@staticmethod
|
|
def get_definition(context):
|
|
delta_type = FixedPoint(Short, 12) \
|
|
if context.protocol_later_eq(106) else \
|
|
FixedPoint(Byte)
|
|
return [
|
|
{'entity_id': VarInt},
|
|
{'delta_x_float': delta_type},
|
|
{'delta_y_float': delta_type},
|
|
{'delta_z_float': delta_type},
|
|
{'on_ground': Boolean},
|
|
]
|
|
|
|
# The following transforms are retained for backward compatibility;
|
|
# they represent the delta values as fixed-point integers with 12 bits
|
|
# of fractional part, regardless of the protocol version.
|
|
delta_x = attribute_transform(
|
|
'delta_x_float', lambda x: int(x * 4096), lambda x: x / 4096)
|
|
delta_y = attribute_transform(
|
|
'delta_y_float', lambda y: int(y * 4096), lambda y: y / 4096)
|
|
delta_z = attribute_transform(
|
|
'delta_z_float', lambda z: int(z * 4096), lambda z: z / 4096)
|
|
|
|
|
|
class TimeUpdatePacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x4E if context.protocol_later_eq(721) else \
|
|
0x4F if context.protocol_later_eq(550) else \
|
|
0x4E if context.protocol_later_eq(471) else \
|
|
0x4A if context.protocol_later_eq(461) else \
|
|
0x4B if context.protocol_later_eq(451) else \
|
|
0x4A if context.protocol_later_eq(389) else \
|
|
0x49 if context.protocol_later_eq(352) else \
|
|
0x48 if context.protocol_later_eq(345) else \
|
|
0x47 if context.protocol_later_eq(336) else \
|
|
0x46 if context.protocol_later_eq(318) else \
|
|
0x44 if context.protocol_later_eq(94) else \
|
|
0x43 if context.protocol_later_eq(70) else \
|
|
0x03
|
|
|
|
packet_name = "time update"
|
|
get_definition = staticmethod(lambda context: [
|
|
{'world_age': Long},
|
|
{'time_of_day': Long},
|
|
])
|
|
|
|
|
|
class UpdateHealthPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x49 if context.protocol_later_eq(721) else \
|
|
0x4A if context.protocol_later_eq(707) else \
|
|
0x49 if context.protocol_later_eq(550) else \
|
|
0x48 if context.protocol_later_eq(471) else \
|
|
0x44 if context.protocol_later_eq(461) else \
|
|
0x45 if context.protocol_later_eq(451) else \
|
|
0x44 if context.protocol_later_eq(389) else \
|
|
0x43 if context.protocol_later_eq(352) else \
|
|
0x42 if context.protocol_later_eq(345) else \
|
|
0x41 if context.protocol_later_eq(336) else \
|
|
0x40 if context.protocol_later_eq(318) else \
|
|
0x3E if context.protocol_later_eq(86) else \
|
|
0x3F if context.protocol_later_eq(77) else \
|
|
0x3E if context.protocol_later_eq(67) else \
|
|
0x06
|
|
|
|
packet_name = 'update health'
|
|
get_definition = staticmethod(lambda context: [
|
|
{'health': Float},
|
|
{'food': VarInt},
|
|
{'food_saturation': Float}
|
|
])
|
|
|
|
|
|
class PluginMessagePacket(AbstractPluginMessagePacket):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x17 if context.protocol_later_eq(741) else \
|
|
0x18 if context.protocol_later_eq(721) else \
|
|
0x19 if context.protocol_later_eq(550) else \
|
|
0x18 if context.protocol_later_eq(471) else \
|
|
0x19 if context.protocol_later_eq(345) else \
|
|
0x18 if context.protocol_later_eq(332) else \
|
|
0x19 if context.protocol_later_eq(318) else \
|
|
0x18 if context.protocol_later_eq(70) else \
|
|
0x3F
|
|
|
|
|
|
class PlayerListHeaderAndFooterPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x53 if context.protocol_later_eq(721) else \
|
|
0x54 if context.protocol_later_eq(550) else \
|
|
0x53 if context.protocol_later_eq(471) else \
|
|
0x5F if context.protocol_later_eq(461) else \
|
|
0x50 if context.protocol_later_eq(451) else \
|
|
0x4F if context.protocol_later_eq(441) else \
|
|
0x4E if context.protocol_later_eq(393) else \
|
|
0x4A if context.protocol_later_eq(338) else \
|
|
0x49 if context.protocol_later_eq(335) else \
|
|
0x47 if context.protocol_later_eq(110) else \
|
|
0x48 if context.protocol_later_eq(107) else \
|
|
0x47
|
|
|
|
packet_name = 'player list header and footer'
|
|
definition = [
|
|
{'header': String},
|
|
{'footer': String}]
|
|
|
|
|
|
class EntityLookPacket(Packet):
|
|
@staticmethod
|
|
def get_id(context):
|
|
return 0x29 if context.protocol_later_eq(741) else \
|
|
0x2A if context.protocol_later_eq(721) else \
|
|
0x2B if context.protocol_later_eq(550) else \
|
|
0x2A if context.protocol_later_eq(389) else \
|
|
0x29 if context.protocol_later_eq(345) else \
|
|
0x28 if context.protocol_later_eq(318) else \
|
|
0x27 if context.protocol_later_eq(94) else \
|
|
0x28 if context.protocol_later_eq(70) else \
|
|
0x16
|
|
|
|
packet_name = 'entity look'
|
|
definition = [
|
|
{'entity_id': VarInt},
|
|
{'yaw': Angle},
|
|
{'pitch': Angle},
|
|
{'on_ground': Boolean}
|
|
]
|