regorganize packet structure as outlined in https://github.com/ammaraskar/pyCraft/pull/68

This commit is contained in:
TheSnoozer 2017-08-08 15:07:33 +02:00 committed by joo
parent f8781c19c8
commit 8552c6efe5
15 changed files with 1291 additions and 1164 deletions

File diff suppressed because it is too large Load Diff

View 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}]

View File

@ -0,0 +1,6 @@
# Formerly known as state_handshake_clientbound.
def get_packets(context):
packets = {
}
return packets

View 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}]

View 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

View File

@ -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

View 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__()

View File

@ -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

View File

@ -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

View File

@ -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

View 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}]

View File

@ -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}]

View 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}]

View 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

View 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}]