mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-16 07:15:24 +01:00
regorganize packet structure as outlined in https://github.com/ammaraskar/pyCraft/pull/68
This commit is contained in:
parent
f8781c19c8
commit
8552c6efe5
File diff suppressed because it is too large
Load Diff
209
minecraft/networking/packets/__init__.py
Normal file
209
minecraft/networking/packets/__init__.py
Normal file
@ -0,0 +1,209 @@
|
||||
# flake8: noqa
|
||||
|
||||
from io import BytesIO
|
||||
from zlib import compress
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt
|
||||
)
|
||||
|
||||
|
||||
# For backward compatibility, re-export any old names from before the change:
|
||||
|
||||
# Handshake State
|
||||
# ==============
|
||||
from .clientbound.handshake import get_packets as state_handshake_clientbound
|
||||
from .serverbound.handshake import HandShakePacket
|
||||
from .serverbound.handshake import get_packets as state_handshake_serverbound
|
||||
|
||||
# Status State
|
||||
# ==============
|
||||
from .clientbound.status import ResponsePacket
|
||||
from .clientbound.status import PingPacketResponse
|
||||
from .clientbound.status import get_packets as state_status_clientbound
|
||||
from .serverbound.status import RequestPacket
|
||||
from .serverbound.status import PingPacket
|
||||
from .serverbound.status import get_packets as state_status_serverbound
|
||||
|
||||
# Login State
|
||||
# ==============
|
||||
from .clientbound.login import DisconnectPacket
|
||||
from .clientbound.login import EncryptionRequestPacket
|
||||
from .clientbound.login import LoginSuccessPacket
|
||||
from .clientbound.login import SetCompressionPacket
|
||||
from .clientbound.login import get_packets as state_login_clientbound
|
||||
from .serverbound.login import LoginStartPacket
|
||||
from .serverbound.login import EncryptionResponsePacket
|
||||
from .serverbound.login import get_packets as state_login_serverbound
|
||||
|
||||
# Playing State
|
||||
# ==============
|
||||
from .clientbound.play import KeepAlivePacket as KeepAlivePacketClientbound
|
||||
from .serverbound.play import KeepAlivePacket as KeepAlivePacketServerbound
|
||||
from .clientbound.play import JoinGamePacket
|
||||
from .clientbound.play import ChatMessagePacket
|
||||
from .clientbound.play import PlayerPositionAndLookPacket
|
||||
from .clientbound.play import DisconnectPacketPlayState
|
||||
from .clientbound.play import SetCompressionPacketPlayState
|
||||
from .clientbound.play import PlayerListItemPacket
|
||||
from .clientbound.play import MapPacket
|
||||
from .clientbound.play import get_packets as state_playing_clientbound
|
||||
from .serverbound.play import ChatPacket
|
||||
from .serverbound.play import PositionAndLookPacket
|
||||
from .serverbound.play import TeleportConfirmPacket
|
||||
from .serverbound.play import AnimationPacket as AnimationPacketServerbound
|
||||
from .serverbound.play import get_packets as state_playing_serverbound
|
||||
|
||||
|
||||
class PacketBuffer(object):
|
||||
def __init__(self):
|
||||
self.bytes = BytesIO()
|
||||
|
||||
def send(self, value):
|
||||
"""
|
||||
Writes the given bytes to the buffer, designed to emulate socket.send
|
||||
:param value: The bytes to write
|
||||
"""
|
||||
self.bytes.write(value)
|
||||
|
||||
def read(self, length=None):
|
||||
return self.bytes.read(length)
|
||||
|
||||
def recv(self, length=None):
|
||||
return self.read(length)
|
||||
|
||||
def reset(self):
|
||||
self.bytes = BytesIO()
|
||||
|
||||
def reset_cursor(self):
|
||||
self.bytes.seek(0)
|
||||
|
||||
def get_writable(self):
|
||||
return self.bytes.getvalue()
|
||||
|
||||
|
||||
class PacketListener(object):
|
||||
def __init__(self, callback, *args):
|
||||
self.callback = callback
|
||||
self.packets_to_listen = []
|
||||
for arg in args:
|
||||
if issubclass(arg, Packet):
|
||||
self.packets_to_listen.append(arg)
|
||||
|
||||
def call_packet(self, packet):
|
||||
for packet_type in self.packets_to_listen:
|
||||
if isinstance(packet, packet_type):
|
||||
self.callback(packet)
|
||||
|
||||
|
||||
class Packet(object):
|
||||
packet_name = "base"
|
||||
id = None
|
||||
definition = None
|
||||
|
||||
# To define the packet ID, either:
|
||||
# 1. Define the attribute `id', of type int, in a subclass; or
|
||||
# 2. Override `get_id' in a subclass and return the correct packet ID
|
||||
# for the given ConnectionContext. This is necessary if the packet ID
|
||||
# has changed across protocol versions, for example.
|
||||
@classmethod
|
||||
def get_id(cls, context):
|
||||
return cls.id
|
||||
|
||||
# To define the network data layout of a packet, either:
|
||||
# 1. Define the attribute `definition', a list of fields, each of which
|
||||
# is a dict mapping attribute names to data types; or
|
||||
# 2. Override `get_definition' in a subclass and return the correct
|
||||
# definition for the given ConnectionContext. This may be necessary
|
||||
# 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
|
||||
# necessary if the packet layout cannot be described as a list of
|
||||
# fields.
|
||||
@classmethod
|
||||
def get_definition(cls, context):
|
||||
return cls.definition
|
||||
|
||||
def __init__(self, context=None, **kwargs):
|
||||
self.context = context
|
||||
self.set_values(**kwargs)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
return self._context
|
||||
|
||||
@context.setter
|
||||
def context(self, _context):
|
||||
self._context = _context
|
||||
self._context_changed()
|
||||
|
||||
def _context_changed(self):
|
||||
if self._context is not None:
|
||||
self.id = self.get_id(self._context)
|
||||
self.definition = self.get_definition(self._context)
|
||||
else:
|
||||
self.id = None
|
||||
self.definition = None
|
||||
|
||||
def set_values(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
return self
|
||||
|
||||
def read(self, file_object):
|
||||
for field in self.definition:
|
||||
for var_name, data_type in field.items():
|
||||
value = data_type.read(file_object)
|
||||
setattr(self, var_name, value)
|
||||
|
||||
# Writes a packet buffer to the socket with the appropriate headers
|
||||
# and compressing the data if necessary
|
||||
def _write_buffer(self, socket, packet_buffer, compression_threshold):
|
||||
# compression_threshold of None means compression is disabled
|
||||
if compression_threshold is not None:
|
||||
if len(packet_buffer.get_writable()) > compression_threshold != -1:
|
||||
# compress the current payload
|
||||
packet_data = packet_buffer.get_writable()
|
||||
compressed_data = compress(packet_data)
|
||||
packet_buffer.reset()
|
||||
# write out the length of the uncompressed payload
|
||||
VarInt.send(len(packet_data), packet_buffer)
|
||||
# write the compressed payload itself
|
||||
packet_buffer.send(compressed_data)
|
||||
else:
|
||||
# write out a 0 to indicate uncompressed data
|
||||
packet_data = packet_buffer.get_writable()
|
||||
packet_buffer.reset()
|
||||
VarInt.send(0, packet_buffer)
|
||||
packet_buffer.send(packet_data)
|
||||
|
||||
VarInt.send(len(packet_buffer.get_writable()), socket) # Packet Size
|
||||
socket.send(packet_buffer.get_writable()) # Packet Payload
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
# buffer the data since we need to know the length of each packet's
|
||||
# payload
|
||||
packet_buffer = PacketBuffer()
|
||||
# write packet's id right off the bat in the header
|
||||
VarInt.send(self.id, packet_buffer)
|
||||
# write every individual field
|
||||
for field in self.definition:
|
||||
for var_name, data_type in field.items():
|
||||
data = getattr(self, var_name)
|
||||
data_type.send(data, packet_buffer)
|
||||
|
||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
||||
|
||||
def __str__(self):
|
||||
str = type(self).__name__
|
||||
if self.id is not None:
|
||||
str = '0x%02X %s' % (self.id, str)
|
||||
if self.definition is not None:
|
||||
fields = {a: getattr(self, a) for d in self.definition for a in d}
|
||||
str = '%s %s' % (str, fields)
|
||||
return str
|
||||
|
||||
|
||||
class KeepAlivePacket(Packet):
|
||||
packet_name = "keep alive"
|
||||
definition = [
|
||||
{'keep_alive_id': VarInt}]
|
@ -0,0 +1,6 @@
|
||||
# Formerly known as state_handshake_clientbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
|
||||
}
|
||||
return packets
|
47
minecraft/networking/packets/clientbound/login/__init__.py
Normal file
47
minecraft/networking/packets/clientbound/login/__init__.py
Normal file
@ -0,0 +1,47 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
String, VarIntPrefixedByteArray, VarInt
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_login_clientbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
DisconnectPacket,
|
||||
EncryptionRequestPacket,
|
||||
LoginSuccessPacket,
|
||||
SetCompressionPacket
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class DisconnectPacket(Packet):
|
||||
id = 0x00
|
||||
packet_name = "disconnect"
|
||||
definition = [
|
||||
{'json_data': String}]
|
||||
|
||||
|
||||
class EncryptionRequestPacket(Packet):
|
||||
id = 0x01
|
||||
packet_name = "encryption request"
|
||||
definition = [
|
||||
{'server_id': String},
|
||||
{'public_key': VarIntPrefixedByteArray},
|
||||
{'verify_token': VarIntPrefixedByteArray}]
|
||||
|
||||
|
||||
class LoginSuccessPacket(Packet):
|
||||
id = 0x02
|
||||
packet_name = "login success"
|
||||
definition = [
|
||||
{'UUID': String},
|
||||
{'Username': String}]
|
||||
|
||||
|
||||
class SetCompressionPacket(Packet):
|
||||
id = 0x03
|
||||
packet_name = "set compression"
|
||||
definition = [
|
||||
{'threshold': VarInt}]
|
281
minecraft/networking/packets/clientbound/play/__init__.py
Normal file
281
minecraft/networking/packets/clientbound/play/__init__.py
Normal file
@ -0,0 +1,281 @@
|
||||
from minecraft.networking.packets import (
|
||||
Packet, PacketBuffer, KeepAlivePacket as AbstractKeepAlivePacket
|
||||
)
|
||||
|
||||
from minecraft.networking.types import (
|
||||
Integer, UnsignedByte, Byte, Boolean, UUID, Short, Position,
|
||||
VarInt, Double, Float, String
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
# Formerly known as state_playing_clientbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
KeepAlivePacket,
|
||||
JoinGamePacket,
|
||||
ChatMessagePacket,
|
||||
PlayerPositionAndLookPacket,
|
||||
MapPacket,
|
||||
PlayerListItemPacket,
|
||||
DisconnectPacketPlayState,
|
||||
SpawnPlayerPacket,
|
||||
EntityVelocityPacket,
|
||||
UpdateHealthPacket,
|
||||
CombatEventPacket,
|
||||
ExplosionPacket,
|
||||
SpawnObjectPacket,
|
||||
BlockChangePacket,
|
||||
MultiBlockChangePacket,
|
||||
}
|
||||
if context.protocol_version <= 47:
|
||||
packets |= {
|
||||
SetCompressionPacketPlayState,
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class KeepAlivePacket(AbstractKeepAlivePacket):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x1F if context.protocol_version >= 332 else \
|
||||
0x20 if context.protocol_version >= 318 else \
|
||||
0x1F if context.protocol_version >= 107 else \
|
||||
0x00
|
||||
|
||||
|
||||
class JoinGamePacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x23 if context.protocol_version >= 332 else \
|
||||
0x24 if context.protocol_version >= 318 else \
|
||||
0x23 if context.protocol_version >= 107 else \
|
||||
0x01
|
||||
|
||||
packet_name = "join game"
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'entity_id': Integer},
|
||||
{'game_mode': UnsignedByte},
|
||||
{'dimension': Integer if context.protocol_version >= 108 else Byte},
|
||||
{'difficulty': UnsignedByte},
|
||||
{'max_players': UnsignedByte},
|
||||
{'level_type': String},
|
||||
{'reduced_debug_info': Boolean}])
|
||||
|
||||
|
||||
class ChatMessagePacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x0F if context.protocol_version >= 332 else \
|
||||
0x10 if context.protocol_version >= 317 else \
|
||||
0x0F if context.protocol_version >= 107 else \
|
||||
0x02
|
||||
|
||||
packet_name = "chat message"
|
||||
definition = [
|
||||
{'json_data': String},
|
||||
{'position': Byte}]
|
||||
|
||||
|
||||
class DisconnectPacketPlayState(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x1A if context.protocol_version >= 332 else \
|
||||
0x1B if context.protocol_version >= 318 else \
|
||||
0x1A if context.protocol_version >= 107 else \
|
||||
0x40
|
||||
|
||||
packet_name = "disconnect"
|
||||
|
||||
definition = [
|
||||
{'json_data': String}]
|
||||
|
||||
|
||||
class SetCompressionPacketPlayState(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 0x05 if context.protocol_version >= 67 else \
|
||||
0x0C
|
||||
|
||||
packet_name = 'spawn player'
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'entity_id': VarInt},
|
||||
{'player_UUID': UUID},
|
||||
{'x': Double} if context.protocol_version >= 100 else {'x': Integer},
|
||||
{'y': Double} if context.protocol_version >= 100 else {'y': Integer},
|
||||
{'z': Double} if context.protocol_version >= 100 else {'z': Integer},
|
||||
{'yaw': Float},
|
||||
{'pitch': Float},
|
||||
# TODO: read entity metadata
|
||||
{'current_item': Short} if context.protocol_version <= 49 else {}
|
||||
])
|
||||
|
||||
|
||||
class EntityVelocityPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x3E if context.protocol_version >= 336 else \
|
||||
0x3D if context.protocol_version >= 332 else \
|
||||
0x3B if context.protocol_version >= 86 else \
|
||||
0x3C if context.protocol_version >= 77 else \
|
||||
0x3B if context.protocol_version >= 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 UpdateHealthPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x41 if context.protocol_version >= 336 else \
|
||||
0x40 if context.protocol_version >= 318 else \
|
||||
0x3E if context.protocol_version >= 86 else \
|
||||
0x3F if context.protocol_version >= 77 else \
|
||||
0x3E if context.protocol_version >= 67 else \
|
||||
0x06
|
||||
|
||||
packet_name = 'update health'
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'health': Float},
|
||||
{'food': VarInt},
|
||||
{'food_saturation': Float}
|
||||
])
|
||||
|
||||
|
||||
class ExplosionPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 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(object):
|
||||
__slots__ = 'x', 'y', 'z'
|
||||
|
||||
def __init__(self, x, y, z):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def __repr__(self):
|
||||
return ('Record(x=%s, y=%s, z=%s)'
|
||||
% (self.x, self.y, self.z))
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
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 BlockChangePacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x0B if context.protocol_version >= 332 else \
|
||||
0x0C if context.protocol_version >= 318 else \
|
||||
0x0B if context.protocol_version >= 67 else \
|
||||
0x24 if context.protocol_version >= 62 else \
|
||||
0x23
|
||||
|
||||
packet_name = 'block change'
|
||||
|
||||
def read(self, file_object):
|
||||
self.location = Position.read(file_object)
|
||||
blockData = VarInt.read(file_object)
|
||||
self.blockId = (blockData >> 4)
|
||||
self.blockMeta = (blockData & 0xF)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
packet_buffer = PacketBuffer()
|
||||
x = self.location['x']
|
||||
y = self.location['y']
|
||||
z = self.location['z']
|
||||
Position.send(x, y, z, packet_buffer)
|
||||
blockData = ((self.blockId << 4) | (self.blockMeta & 0xF))
|
||||
VarInt.send(blockData)
|
||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
||||
|
||||
|
||||
class MultiBlockChangePacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x10 if context.protocol_version >= 332 else \
|
||||
0x11 if context.protocol_version >= 318 else \
|
||||
0x10 if context.protocol_version >= 67 else \
|
||||
0x22
|
||||
|
||||
packet_name = 'multi block change'
|
||||
|
||||
class Record(object):
|
||||
__slots__ = 'x', 'y', 'z', 'blockId', 'blockMeta'
|
||||
|
||||
def __init__(self, horizontal_position, y_coordinate, blockData):
|
||||
self.x = (horizontal_position & 0xF0) >> 4
|
||||
self.y = y_coordinate
|
||||
self.z = (horizontal_position & 0x0F)
|
||||
self.blockId = (blockData >> 4)
|
||||
self.blockMeta = (blockData & 0xF)
|
||||
|
||||
def __repr__(self):
|
||||
return ('Record(x=%s, y=%s, z=%s, blockId=%s)'
|
||||
% (self.x, self.y, self.z, self.blockId))
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def read(self, file_object):
|
||||
self.chunk_x = Integer.read(file_object)
|
||||
self.chunk_z = Integer.read(file_object)
|
||||
records_count = VarInt.read(file_object)
|
||||
self.records = []
|
||||
for i in range(records_count):
|
||||
rec_horizontal_position = UnsignedByte.read(file_object)
|
||||
rec_y_coordinate = UnsignedByte.read(file_object)
|
||||
rec_blockData = VarInt.read(file_object)
|
||||
record = MultiBlockChangePacket.Record(
|
||||
rec_horizontal_position,
|
||||
rec_y_coordinate, rec_blockData)
|
||||
self.records.append(record)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
raise NotImplementedError
|
@ -0,0 +1,65 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt, Integer, String
|
||||
)
|
||||
|
||||
|
||||
class CombatEventPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x2D if context.protocol_version >= 336 else \
|
||||
0x2C if context.protocol_version >= 332 else \
|
||||
0x2D if context.protocol_version >= 318 else \
|
||||
0x2C if context.protocol_version >= 86 else \
|
||||
0x2D if context.protocol_version >= 80 else \
|
||||
0x2C if context.protocol_version >= 67 else \
|
||||
0x42
|
||||
|
||||
packet_name = 'combat event'
|
||||
|
||||
class EventType(object):
|
||||
def read(self, file_object):
|
||||
self._read(file_object)
|
||||
|
||||
def _read(self, file_object):
|
||||
raise NotImplementedError(
|
||||
'This abstract method must be overridden in a subclass.')
|
||||
|
||||
@classmethod
|
||||
def type_from_id(cls, event_id):
|
||||
subcls = {
|
||||
0: CombatEventPacket.EnterCombatEvent,
|
||||
1: CombatEventPacket.EndCombatEvent,
|
||||
2: CombatEventPacket.EntityDeadEvent
|
||||
}.get(event_id)
|
||||
if subcls is None:
|
||||
raise ValueError("Unknown combat event ID: %s."
|
||||
% event_id)
|
||||
return subcls
|
||||
|
||||
class EnterCombatEvent(EventType):
|
||||
def _read(self, file_object):
|
||||
pass
|
||||
|
||||
class EndCombatEvent(EventType):
|
||||
__slots__ = 'duration', 'entity_id'
|
||||
|
||||
def _read(self, file_object):
|
||||
self.duration = VarInt.read(file_object)
|
||||
self.entity_id = Integer.read(file_object)
|
||||
|
||||
class EntityDeadEvent(EventType):
|
||||
__slots__ = 'player_id', 'entity_id', 'message'
|
||||
|
||||
def _read(self, file_object):
|
||||
self.player_id = VarInt.read(file_object)
|
||||
self.entity_id = Integer.read(file_object)
|
||||
self.message = String.read(file_object)
|
||||
|
||||
def read(self, file_object):
|
||||
event_id = VarInt.read(file_object)
|
||||
self.event_type = CombatEventPacket.EventType.type_from_id(event_id)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
raise NotImplementedError
|
148
minecraft/networking/packets/clientbound/play/map_packet.py
Normal file
148
minecraft/networking/packets/clientbound/play/map_packet.py
Normal file
@ -0,0 +1,148 @@
|
||||
from minecraft.networking.packets import (
|
||||
Packet, PacketBuffer
|
||||
)
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray
|
||||
)
|
||||
|
||||
|
||||
class MapPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x24 if context.protocol_version >= 334 else \
|
||||
0x25 if context.protocol_version >= 318 else \
|
||||
0x24 if context.protocol_version >= 107 else \
|
||||
0x34
|
||||
|
||||
packet_name = 'map'
|
||||
|
||||
class MapIcon(object):
|
||||
__slots__ = 'type', 'direction', 'location'
|
||||
|
||||
def __init__(self, type, direction, location):
|
||||
self.type = type
|
||||
self.direction = direction
|
||||
self.location = location
|
||||
|
||||
def __repr__(self):
|
||||
return ('MapIcon(type=%s, direction=%s, location=%s)'
|
||||
% (self.type, self.direction, self.location))
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
class Map(object):
|
||||
__slots__ = ('id', 'scale', 'icons', 'pixels', 'width', 'height',
|
||||
'is_tracking_position')
|
||||
|
||||
def __init__(self, id=None, scale=None, width=128, height=128):
|
||||
self.id = id
|
||||
self.scale = scale
|
||||
self.icons = []
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.pixels = bytearray(0 for i in range(width*height))
|
||||
self.is_tracking_position = True
|
||||
|
||||
def __repr__(self):
|
||||
return ('Map(id=%s, scale=%s, icons=%s, width=%s, height=%s)' % (
|
||||
self.id, self.scale, self.icons, self.width, self.height))
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
class MapSet(object):
|
||||
__slots__ = 'maps_by_id'
|
||||
|
||||
def __init__(self):
|
||||
self.maps_by_id = dict()
|
||||
|
||||
def __repr__(self):
|
||||
maps = [str(map) for map in self.maps_by_id.values()]
|
||||
return 'MapSet(%s)' % ', '.join(maps)
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def read(self, file_object):
|
||||
self.map_id = VarInt.read(file_object)
|
||||
self.scale = Byte.read(file_object)
|
||||
|
||||
if self.context.protocol_version >= 107:
|
||||
self.is_tracking_position = Boolean.read(file_object)
|
||||
else:
|
||||
self.is_tracking_position = True
|
||||
|
||||
icon_count = VarInt.read(file_object)
|
||||
self.icons = []
|
||||
for i in range(icon_count):
|
||||
type, direction = divmod(UnsignedByte.read(file_object), 16)
|
||||
x = Byte.read(file_object)
|
||||
z = Byte.read(file_object)
|
||||
icon = MapPacket.MapIcon(type, direction, (x, z))
|
||||
self.icons.append(icon)
|
||||
self.width = UnsignedByte.read(file_object)
|
||||
if self.width:
|
||||
self.height = UnsignedByte.read(file_object)
|
||||
x = Byte.read(file_object)
|
||||
z = Byte.read(file_object)
|
||||
self.offset = (x, z)
|
||||
self.pixels = VarIntPrefixedByteArray.read(file_object)
|
||||
else:
|
||||
self.height = 0
|
||||
self.offset = None
|
||||
self.pixels = None
|
||||
|
||||
def apply_to_map(self, map):
|
||||
map.id = self.map_id
|
||||
map.scale = self.scale
|
||||
map.icons[:] = self.icons
|
||||
if self.pixels is not None:
|
||||
for i in range(len(self.pixels)):
|
||||
x = self.offset[0] + i % self.width
|
||||
z = self.offset[1] + i // self.width
|
||||
map.pixels[x + map.width * z] = self.pixels[i]
|
||||
map.is_tracking_position = self.is_tracking_position
|
||||
|
||||
def apply_to_map_set(self, map_set):
|
||||
map = map_set.maps_by_id.get(self.map_id)
|
||||
if map is None:
|
||||
map = MapPacket.Map(self.map_id)
|
||||
map_set.maps_by_id[self.map_id] = map
|
||||
self.apply_to_map(map)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
packet_buffer = PacketBuffer()
|
||||
VarInt.send(self.id, packet_buffer)
|
||||
|
||||
VarInt.send(self.map_id, packet_buffer)
|
||||
Byte.send(self.scale, packet_buffer)
|
||||
if self.context.protocol_version >= 107:
|
||||
Boolean.send(self.is_tracking_position, packet_buffer)
|
||||
|
||||
VarInt.send(len(self.icons), packet_buffer)
|
||||
for icon in self.icons:
|
||||
type_and_direction = (icon.direction << 4) & 0xF0
|
||||
type_and_direction |= (icon.type & 0xF)
|
||||
UnsignedByte.send(type_and_direction, packet_buffer)
|
||||
Byte.send(icon.location[0], packet_buffer)
|
||||
Byte.send(icon.location[1], packet_buffer)
|
||||
|
||||
UnsignedByte.send(self.width, packet_buffer)
|
||||
if self.width:
|
||||
UnsignedByte.send(self.height, packet_buffer)
|
||||
UnsignedByte.send(self.offset[0], packet_buffer) # x
|
||||
UnsignedByte.send(self.offset[1], packet_buffer) # z
|
||||
VarIntPrefixedByteArray.send(self.pixels, packet_buffer)
|
||||
|
||||
self._write_buffer(socket, packet_buffer, compression_threshold)
|
||||
|
||||
def __repr__(self):
|
||||
return 'MapPacket(%s)' % ', '.join(
|
||||
'%s=%r' % (k, v)
|
||||
for (k, v) in self.__dict__.items()
|
||||
if k != 'pixels')
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
@ -0,0 +1,159 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
String, Boolean, UUID, VarInt
|
||||
)
|
||||
|
||||
|
||||
class PlayerListItemPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x2E if context.protocol_version >= 336 else \
|
||||
0x2D if context.protocol_version >= 332 else \
|
||||
0x2E if context.protocol_version >= 318 else \
|
||||
0x2D if context.protocol_version >= 107 else \
|
||||
0x38
|
||||
|
||||
packet_name = "player list item"
|
||||
|
||||
class PlayerList(object):
|
||||
__slots__ = 'players_by_uuid'
|
||||
|
||||
def __init__(self):
|
||||
self.players_by_uuid = dict()
|
||||
|
||||
class PlayerListItem(object):
|
||||
__slots__ = (
|
||||
'uuid', 'name', 'properties', 'gamemode', 'ping', 'display_name')
|
||||
|
||||
def __init__(self, **kwds):
|
||||
for key, val in kwds.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
class PlayerProperty(object):
|
||||
__slots__ = 'name', 'value', 'signature'
|
||||
|
||||
def read(self, file_object):
|
||||
self.name = String.read(file_object)
|
||||
self.value = String.read(file_object)
|
||||
is_signed = Boolean.read(file_object)
|
||||
if is_signed:
|
||||
self.signature = String.read(file_object)
|
||||
else:
|
||||
self.signature = None
|
||||
|
||||
class Action(object):
|
||||
__slots__ = 'uuid'
|
||||
|
||||
def read(self, file_object):
|
||||
self.uuid = UUID.read(file_object)
|
||||
self._read(file_object)
|
||||
|
||||
def _read(self, file_object):
|
||||
raise NotImplementedError(
|
||||
'This abstract method must be overridden in a subclass.')
|
||||
|
||||
@classmethod
|
||||
def type_from_id(cls, action_id):
|
||||
subcls = {
|
||||
0: PlayerListItemPacket.AddPlayerAction,
|
||||
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
|
||||
|
||||
class AddPlayerAction(Action):
|
||||
__slots__ = 'name', 'properties', 'gamemode', 'ping', 'display_name'
|
||||
|
||||
def _read(self, file_object):
|
||||
self.name = String.read(file_object)
|
||||
prop_count = VarInt.read(file_object)
|
||||
self.properties = []
|
||||
for i in range(prop_count):
|
||||
property = PlayerListItemPacket.PlayerProperty()
|
||||
property.read(file_object)
|
||||
self.properties.append(property)
|
||||
self.gamemode = VarInt.read(file_object)
|
||||
self.ping = VarInt.read(file_object)
|
||||
has_display_name = Boolean.read(file_object)
|
||||
if has_display_name:
|
||||
self.display_name = String.read(file_object)
|
||||
else:
|
||||
self.display_name = None
|
||||
|
||||
def apply(self, player_list):
|
||||
player = PlayerListItemPacket.PlayerListItem(
|
||||
uuid=self.uuid,
|
||||
name=self.name,
|
||||
properties=self.properties,
|
||||
gamemode=self.gamemode,
|
||||
ping=self.ping,
|
||||
display_name=self.display_name)
|
||||
player_list.players_by_uuid[self.uuid] = player
|
||||
|
||||
class UpdateGameModeAction(Action):
|
||||
__slots__ = 'gamemode'
|
||||
|
||||
def _read(self, file_object):
|
||||
self.gamemode = VarInt.read(file_object)
|
||||
|
||||
def apply(self, player_list):
|
||||
player = player_list.players_by_uuid.get(self.uuid)
|
||||
if player:
|
||||
player.gamemode = self.gamemode
|
||||
|
||||
class UpdateLatencyAction(Action):
|
||||
__slots__ = 'ping'
|
||||
|
||||
def _read(self, file_object):
|
||||
self.ping = VarInt.read(file_object)
|
||||
|
||||
def apply(self, player_list):
|
||||
player = player_list.players_by_uuid.get(self.uuid)
|
||||
if player:
|
||||
player.ping = self.ping
|
||||
|
||||
class UpdateDisplayNameAction(Action):
|
||||
__slots__ = 'display_name'
|
||||
|
||||
def _read(self, file_object):
|
||||
has_display_name = Boolean.read(file_object)
|
||||
if has_display_name:
|
||||
self.display_name = String.read(file_object)
|
||||
else:
|
||||
self.display_name = None
|
||||
|
||||
def apply(self, player_list):
|
||||
player = player_list.players_by_uuid.get(self.uuid)
|
||||
if player:
|
||||
player.display_name = self.display_name
|
||||
|
||||
class RemovePlayerAction(Action):
|
||||
def _read(self, file_object):
|
||||
pass
|
||||
|
||||
def apply(self, player_list):
|
||||
if self.uuid in player_list.players_by_uuid:
|
||||
del player_list.players_by_uuid[self.uuid]
|
||||
|
||||
def read(self, file_object):
|
||||
action_id = VarInt.read(file_object)
|
||||
self.action_type = PlayerListItemPacket.Action.type_from_id(action_id)
|
||||
action_count = VarInt.read(file_object)
|
||||
self.actions = []
|
||||
for i in range(action_count):
|
||||
action = self.action_type()
|
||||
action.read(file_object)
|
||||
self.actions.append(action)
|
||||
|
||||
def apply(self, player_list):
|
||||
for action in self.actions:
|
||||
action.apply(player_list)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
raise NotImplementedError
|
@ -0,0 +1,69 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
Double, Float, Byte, VarInt
|
||||
)
|
||||
|
||||
|
||||
class PlayerPositionAndLookPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x2F if context.protocol_version >= 336 else \
|
||||
0x2E if context.protocol_version >= 318 else \
|
||||
0x2F if context.protocol_version >= 107 else \
|
||||
0x08
|
||||
|
||||
packet_name = "player position and look"
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'x': Double},
|
||||
{'y': Double},
|
||||
{'z': Double},
|
||||
{'yaw': Float},
|
||||
{'pitch': Float},
|
||||
{'flags': Byte},
|
||||
{'teleport_id': VarInt} if context.protocol_version >= 107 else {},
|
||||
])
|
||||
|
||||
FLAG_REL_X = 0x01
|
||||
FLAG_REL_Y = 0x02
|
||||
FLAG_REL_Z = 0x04
|
||||
FLAG_REL_YAW = 0x08
|
||||
FLAG_REL_PITCH = 0x10
|
||||
|
||||
class PositionAndLook(object):
|
||||
__slots__ = 'x', 'y', 'z', 'yaw', 'pitch'
|
||||
|
||||
def __init__(self, **kwds):
|
||||
for attr in self.__slots__:
|
||||
setattr(self, attr, kwds.get(attr))
|
||||
|
||||
# Update a PositionAndLook instance using this packet.
|
||||
def apply(self, target):
|
||||
# pylint: disable=no-member
|
||||
if self.flags & self.FLAG_REL_X:
|
||||
target.x += self.x
|
||||
else:
|
||||
target.x = self.x
|
||||
|
||||
if self.flags & self.FLAG_REL_Y:
|
||||
target.y += self.y
|
||||
else:
|
||||
target.y = self.y
|
||||
|
||||
if self.flags & self.FLAG_REL_Z:
|
||||
target.z += self.z
|
||||
else:
|
||||
target.z = self.z
|
||||
|
||||
if self.flags & self.FLAG_REL_YAW:
|
||||
target.yaw += self.yaw
|
||||
else:
|
||||
target.yaw = self.yaw
|
||||
|
||||
if self.flags & self.FLAG_REL_PITCH:
|
||||
target.pitch += self.pitch
|
||||
else:
|
||||
target.pitch = self.pitch
|
||||
|
||||
target.yaw %= 360
|
||||
target.pitch %= 360
|
@ -0,0 +1,83 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt, UUID, Byte, Double, Integer, UnsignedByte, Short
|
||||
)
|
||||
|
||||
|
||||
class SpawnObjectPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x00 if context.protocol_version >= 67 else \
|
||||
0x0E
|
||||
|
||||
packet_name = 'spawn object'
|
||||
|
||||
class EntityType:
|
||||
BOAT = 1
|
||||
ITEM_STACK = 2
|
||||
AREA_EFFECT_CLOUD = 3
|
||||
MINECART = 10
|
||||
ACTIVATED_TNT = 50
|
||||
ENDERCRYSTAL = 51
|
||||
ARROW = 60
|
||||
SNOWBALL = 61
|
||||
EGG = 62
|
||||
FIREBALL = 63
|
||||
FIRECHARGE = 64
|
||||
ENDERPERL = 65
|
||||
WITHER_SKULL = 66
|
||||
SHULKER_BULLET = 67
|
||||
LLAMA_SPIT = 68
|
||||
FALLING_OBJECT = 70
|
||||
ITEM_FRAMES = 71
|
||||
EYE_OF_ENDER = 72
|
||||
POTION = 73
|
||||
EXP_BOTTLE = 75
|
||||
FIREWORK_ROCKET = 76
|
||||
LEASH_KNOT = 77
|
||||
ARMORSTAND = 78
|
||||
EVOCATION_FANGS = 79
|
||||
FISHING_HOOK = 90
|
||||
SPECTRAL_ARROW = 91
|
||||
DRAGON_FIREBALL = 93
|
||||
|
||||
@classmethod
|
||||
def get_type_by_id(cls, type_id):
|
||||
by_id = {id: entity for (entity, id) in
|
||||
cls.__dict__.items() if entity.isupper()}
|
||||
|
||||
return by_id[type_id]
|
||||
|
||||
def read(self, file_object):
|
||||
self.entity_id = VarInt.read(file_object)
|
||||
if self._context.protocol_version >= 49:
|
||||
self.objectUUID = UUID.read(file_object)
|
||||
type_id = Byte.read(file_object)
|
||||
self.type = SpawnObjectPacket.EntityType.get_type_by_id(type_id)
|
||||
|
||||
if self._context.protocol_version >= 100:
|
||||
self.x = Double.read(file_object)
|
||||
self.y = Double.read(file_object)
|
||||
self.z = Double.read(file_object)
|
||||
else:
|
||||
self.x = Integer.read(file_object)
|
||||
self.y = Integer.read(file_object)
|
||||
self.z = Integer.read(file_object)
|
||||
|
||||
self.pitch = UnsignedByte.read(file_object)
|
||||
self.yaw = UnsignedByte.read(file_object)
|
||||
self.data = Integer.read(file_object)
|
||||
|
||||
if self._context.protocol_version < 49:
|
||||
if self.data > 0:
|
||||
self.velocity_x = Short.read(file_object)
|
||||
self.velocity_y = Short.read(file_object)
|
||||
self.velocity_z = Short.read(file_object)
|
||||
else:
|
||||
self.velocity_x = Short.read(file_object)
|
||||
self.velocity_y = Short.read(file_object)
|
||||
self.velocity_z = Short.read(file_object)
|
||||
|
||||
def write(self, socket, compression_threshold=None):
|
||||
raise NotImplementedError
|
28
minecraft/networking/packets/clientbound/status/__init__.py
Normal file
28
minecraft/networking/packets/clientbound/status/__init__.py
Normal file
@ -0,0 +1,28 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
String, Long
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_status_clientbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
ResponsePacket,
|
||||
PingPacketResponse,
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class ResponsePacket(Packet):
|
||||
id = 0x00
|
||||
packet_name = "response"
|
||||
definition = [
|
||||
{'json_response': String}]
|
||||
|
||||
|
||||
class PingPacketResponse(Packet):
|
||||
id = 0x01
|
||||
packet_name = "ping"
|
||||
definition = [
|
||||
{'time': Long}]
|
@ -0,0 +1,23 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
VarInt, String, UnsignedShort
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_handshake_serverbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
HandShakePacket
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class HandShakePacket(Packet):
|
||||
id = 0x00
|
||||
packet_name = "handshake"
|
||||
definition = [
|
||||
{'protocol_version': VarInt},
|
||||
{'server_address': String},
|
||||
{'server_port': UnsignedShort},
|
||||
{'next_state': VarInt}]
|
29
minecraft/networking/packets/serverbound/login/__init__.py
Normal file
29
minecraft/networking/packets/serverbound/login/__init__.py
Normal file
@ -0,0 +1,29 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
String, VarIntPrefixedByteArray
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_login_serverbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
LoginStartPacket,
|
||||
EncryptionResponsePacket
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class LoginStartPacket(Packet):
|
||||
id = 0x00
|
||||
packet_name = "login start"
|
||||
definition = [
|
||||
{'name': String}]
|
||||
|
||||
|
||||
class EncryptionResponsePacket(Packet):
|
||||
id = 0x01
|
||||
packet_name = "encryption response"
|
||||
definition = [
|
||||
{'shared_secret': VarIntPrefixedByteArray},
|
||||
{'verify_token': VarIntPrefixedByteArray}]
|
117
minecraft/networking/packets/serverbound/play/__init__.py
Normal file
117
minecraft/networking/packets/serverbound/play/__init__.py
Normal file
@ -0,0 +1,117 @@
|
||||
from minecraft.networking.packets import (
|
||||
Packet, KeepAlivePacket as AbstractKeepAlivePacket
|
||||
)
|
||||
|
||||
from minecraft.networking.types import (
|
||||
Double, Float, Boolean, VarInt, String
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_playing_serverbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
KeepAlivePacket,
|
||||
ChatPacket,
|
||||
PositionAndLookPacket,
|
||||
AnimationPacket,
|
||||
ClientStatusPacket,
|
||||
}
|
||||
if context.protocol_version >= 107:
|
||||
packets |= {
|
||||
TeleportConfirmPacket,
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class KeepAlivePacket(AbstractKeepAlivePacket):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x0B if context.protocol_version >= 336 else \
|
||||
0x0C if context.protocol_version >= 318 else \
|
||||
0x0B if context.protocol_version >= 107 else \
|
||||
0x00
|
||||
|
||||
|
||||
class ChatPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x02 if context.protocol_version >= 336 else \
|
||||
0x03 if context.protocol_version >= 318 else \
|
||||
0x02 if context.protocol_version >= 107 else \
|
||||
0x01
|
||||
|
||||
@staticmethod
|
||||
def get_max_length(context):
|
||||
return 256 if context.protocol_version >= 306 else \
|
||||
100
|
||||
|
||||
@property
|
||||
def max_length(self):
|
||||
if self.context is not None:
|
||||
return self.get_max_length(self.context)
|
||||
|
||||
packet_name = "chat"
|
||||
definition = [
|
||||
{'message': String}]
|
||||
|
||||
|
||||
class PositionAndLookPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x0E if context.protocol_version >= 336 else \
|
||||
0x0F if context.protocol_version >= 332 else \
|
||||
0x0E if context.protocol_version >= 318 else \
|
||||
0x0D if context.protocol_version >= 107 else \
|
||||
0x06
|
||||
|
||||
packet_name = "position and look"
|
||||
definition = [
|
||||
{'x': Double},
|
||||
{'feet_y': Double},
|
||||
{'z': Double},
|
||||
{'yaw': Float},
|
||||
{'pitch': Float},
|
||||
{'on_ground': Boolean}]
|
||||
|
||||
|
||||
class TeleportConfirmPacket(Packet):
|
||||
# Note: added between protocol versions 47 and 107.
|
||||
id = 0x00
|
||||
packet_name = "teleport confirm"
|
||||
definition = [
|
||||
{'teleport_id': VarInt}]
|
||||
|
||||
|
||||
class AnimationPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x1D if context.protocol_version >= 332 else \
|
||||
0x1C if context.protocol_version >= 318 else \
|
||||
0x1A if context.protocol_version >= 107 else \
|
||||
0x0A
|
||||
|
||||
packet_name = "animation"
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'hand': VarInt} if context.protocol_version >= 107 else {}])
|
||||
HAND_MAIN = 0
|
||||
HAND_OFF = 1
|
||||
|
||||
|
||||
class ClientStatusPacket(Packet):
|
||||
@staticmethod
|
||||
def get_id(context):
|
||||
return 0x03 if context.protocol_version >= 336 else \
|
||||
0x04 if context.protocol_version >= 318 else \
|
||||
0x03 if context.protocol_version >= 80 else \
|
||||
0x02 if context.protocol_version >= 67 else \
|
||||
0x17 if context.protocol_version >= 49 else \
|
||||
0x16
|
||||
|
||||
packet_name = "client status"
|
||||
get_definition = staticmethod(lambda context: [
|
||||
{'action_id': VarInt}])
|
||||
|
||||
RESPAWN = 0
|
||||
REQUEST_STATS = 1
|
||||
# Note: Open Inventory (id 2) was removed in protocol version 319
|
||||
OPEN_INVENTORY = 2
|
27
minecraft/networking/packets/serverbound/status/__init__.py
Normal file
27
minecraft/networking/packets/serverbound/status/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
from minecraft.networking.packets import Packet
|
||||
|
||||
from minecraft.networking.types import (
|
||||
Long
|
||||
)
|
||||
|
||||
|
||||
# Formerly known as state_status_serverbound.
|
||||
def get_packets(context):
|
||||
packets = {
|
||||
RequestPacket,
|
||||
PingPacket
|
||||
}
|
||||
return packets
|
||||
|
||||
|
||||
class RequestPacket(Packet):
|
||||
id = 0x00
|
||||
packet_name = "request"
|
||||
definition = []
|
||||
|
||||
|
||||
class PingPacket(Packet):
|
||||
id = 0x01
|
||||
packet_name = "ping"
|
||||
definition = [
|
||||
{'time': Long}]
|
Loading…
Reference in New Issue
Block a user