pyCraft/minecraft/networking/packets/serverbound/play/client_settings_packet.py

85 lines
2.9 KiB
Python
Raw Normal View History

import operator
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
String, Byte, VarInt, Boolean, UnsignedByte, Enum, BitFieldEnum,
AbsoluteHand
)
from minecraft.utility import attribute_transform
class ClientSettingsPacket(Packet):
@staticmethod
def get_id(context):
Fix: non-monotonic protocol versions are not correctly handled 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`.
2020-12-02 14:30:40 +01:00
return 0x05 if context.protocol_later_eq(464) else \
0x04 if context.protocol_later_eq(389) else \
0x03 if context.protocol_later_eq(343) else \
0x04 if context.protocol_later_eq(336) else \
0x05 if context.protocol_later_eq(318) else \
0x04 if context.protocol_later_eq(94) else \
0x15
packet_name = 'client settings'
@staticmethod
def get_definition(context):
return [
{'locale': String},
{'view_distance': Byte},
{'chat_mode': VarInt if context.protocol_later(47) else Byte},
{'chat_colors': Boolean},
{'displayed_skin_parts': UnsignedByte},
{'main_hand': VarInt} if context.protocol_later(49) else {},
{'enable_text_filtering': Boolean}
if context.protocol_later_eq(757) else
{'disable_text_filtering': Boolean}
if context.protocol_later_eq(755) else {},
{'allow_server_listings': Boolean}
if context.protocol_later_eq(755) else {},
]
# Set a default value for 'enable_text_filtering', because most clients
# will probably want this value, and to avoid breaking old code.
enable_text_filtering = False
# To support the possibility of both 'enable_text_filtering' and
# 'disable_text_filtering' fields existing, make 'disable_text_filtering'
# (which is the less likely to be used of the two) into a property that
# stores the negation of its value in the ordinary attribute
# 'enable_text_filtering'.
disable_text_filtering = attribute_transform(
'enable_text_filtering', operator.not_, operator.not_)
# Set a default value for 'allow_server_listings', because most clients
# will probably want this value, and to avoid breaking old code.
allow_server_listings = False
field_enum = classmethod(
lambda cls, field, context: {
'chat_mode': cls.ChatMode,
'displayed_skin_parts': cls.SkinParts,
'main_hand': AbsoluteHand,
}.get(field))
class ChatMode(Enum):
FULL = 0 # Receive all types of chat messages.
SYSTEM = 1 # Receive only command results and game information.
NONE = 2 # Receive only game information.
class SkinParts(BitFieldEnum):
CAPE = 0x01
JACKET = 0x02
LEFT_SLEEVE = 0x04
RIGHT_SLEEVE = 0x08
LEFT_PANTS_LEG = 0x10
RIGHT_PANTS_LEG = 0x20
HAT = 0x40
ALL = 0x7F
NONE = 0x00
# This class alias is retained for backward compatibility.
Hand = AbsoluteHand