pak.packets.subpacket¶
Packets which are contained in other Packets.
- class SubPacket(*, ctx=None, **fields)[source]¶
Bases:
PacketA
Packetcontained within anotherPacket.A frequent occurrence in many protocols are
Packets which have certain structures within them which associate data together. This could be just a conceptual distincition, or it could be a more practical distinction. These structures can be thought of as “sub-packets”.For instance, if we had a packet which had a variable amount of pairs of names of players and their levels, represented with a leading
UInt16which reports how many pairs there are, and then aTerminatedStringfor the name and aUInt8for the level, we could define such aSubPacket:import pak class PlayerNameAndLevel(pak.SubPacket): name: pak.TerminatedString level: pak.UInt8
We could then use it in a normal
Packetlike aType:class PlayerInfoPacket(pak.Packet): players: PlayerNameAndLevel[pak.UInt16]
And we can then use it all like so:
packet = PlayerInfoPacket(players=[ PlayerNameAndLevel(name="Bob", level=1), PlayerNameAndLevel(name="Alice", level=2), ]) assert packet.pack() == ( b"\x02\x00" + b"Bob\x00\x01" + b"Alice\x00\x02" )
Note
SubPackets (or rather, its subclasses) are typelike, enabling their use like any otherType.Additionally, the terse array syntax of
ElemType[size]works withSubPackets as well.And because
SubPackets are stillPackets, they can leverage a bit of that functionality, notably with their header.Note
SubPackets cannot define their ownPacket.Context, as they should receive and use the context of their superPacket.SubPacketheaders may only have asizefield and/or anidfield. When aSubPacketheader contains asizefield, then that many bytes will be read from the buffer supplied to the superPacketand used to unpack theSubPacket:import pak class SizedSubPacket(pak.SubPacket): class Header(pak.SubPacket.Header): size: pak.UInt8 data: pak.RawByte[None] class MyPacket(pak.Packet): sized: SizedSubPacket rest: pak.RawByte[None] assert MyPacket.unpack( # The 'rest' data should not be consumed by the 'sized' field. b"\x0Asized data" + b"rest" ) == MyPacket( sized = SizedSubPacket(data=b"sized data"), rest = b"rest", )
When a
SubPacketheader contains anidfield, then a subclass of theSubPacketis searched for usingPacket.subclass_with_id(), and then used to marshal the data:import pak class IDSubPacket(pak.SubPacket): class Header(pak.SubPacket.Header): id: pak.UInt8 class NumberData(IDSubPacket): id = 1 number: pak.UInt16 class ArrayData(IDSubPacket): id = 2 array: pak.UInt8[2] class DataPacket(pak.Packet): data: IDSubPacket assert DataPacket.unpack( b"\x01" + b"\x02\x00" ) == DataPacket( data = NumberData(number=2), ) assert DataPacket.unpack( b"\x02" + b"\x00\x01" ) == DataPacket( data = ArrayData(array=[0, 1]), )
When an unknown ID is encountered, then
_subclass_for_unknown_id()is called, allowing customization for different needs.- exception NoAvailableSubclassError(subpacket_cls, *, id)[source]¶
Bases:
ValueError,UnsuppressedErrorAn error indicating that there was no corresponding subclass of a
SubPacketfor a particular ID.By default,
SubPacket._subclass_for_unknown_id()will throw aSubPacket.NoAvailableSubclassError.
- classmethod _subclass_for_unknown_id(id, *, ctx)[source]¶
Gets the subclass for the
SubPacketwhen an unknown ID is encountered.This method is overridable in order to customize the relevant behavior. For instance, a subclass from
Packet.GenericWithID()orPacket.EmptyWithID()could be returned.By default, a
SubPacket.NoAvailableSubclassErroris raised upon encountering an unknown ID.- Parameters:
id – The unknown ID.
ctx (
Packet.Context) – The context for theSubPacket.
- Returns:
The subclass of the
SubPacket.- Return type:
Subclass of
SubPacket
- class AlignedSubPacket(*, ctx=None, **fields)[source]¶
Bases:
SubPacket,AlignedPacketA
SubPacketwhich aligns its fields.When converted to a
Type, the alignment of theAlignedSubPacketwill be propagated appropriately, namely to its superPacket.Note
If an
AlignedSubPacketdefines its own header, then aTypeErroris raised.