packet
Read, write, send and block game packets, in the spirit of the gLua net library. It lives at xiao.packet.
A packet is named by its id (0 to 1023) or by its name: "TextPacket", "Text" and "text" all resolve to the same id (case does not matter, the Packet suffix is optional). An unknown name raises an error.
Payloads are the bytes after the packet header, in Bedrock wire format (little endian, unsigned varints, zigzag signed varints). The header (id and sub-client bytes) is added by the client on send.
send fails with a message and never raises: no packet seen yet (join a world first), queue full (8 pending sends, retry next frame), network hook not installed.
For the loop that calls these functions each frame, see xiao.signal and Running scripts.
Sending
gLua style, with a current message:
xiao.packet.start("Text")
xiao.packet.write_byte(1)
xiao.packet.write_bool(false)
xiao.packet.write_string("")
local ok, err = xiao.packet.send()
Chainable and reusable:
local w = xiao.packet.writer("Animate")
w:write_varint(1):write_varuint64(runtime_id)
w:send()
print(w:size(), #w:data())
xiao.packet.start(packet: number | string): writer
xiao.packet.start(packet: number | string): writer
- Starts the current message and returns its writer.
xiao.packet.write_*(...)
xiao.packet.write_*(...)
- Writes into the current message, with the same
write_*functions as the writer methods. - Raises an error without
start.
xiao.packet.send(): boolean, string
xiao.packet.send(): boolean, string
- Sends the current message and clears it.
- Returns
trueorfalse, error.
xiao.packet.cancel()
xiao.packet.cancel()
- Drops the current message.
xiao.packet.writer(packet?: number | string): writer
xiao.packet.writer(packet?: number | string): writer
- Returns a new independent writer.
xiao.packet.send_raw(packet: number | string, bytes: string)
xiao.packet.send_raw(packet: number | string, bytes: string)
- Sends a payload already built as a string.
Writer
Each write_* returns the writer.
writer:write_bool(v: boolean): writer
writer:write_bool(v: boolean): writer
- 1 byte, 0 or 1.
writer:write_byte(n: number): writer
writer:write_byte(n: number): writer
- 1 byte, -128 to 255.
writer:write_short(n: number): writer
writer:write_short(n: number): writer
- 2 bytes.
writer:write_ushort(n: number): writer
writer:write_ushort(n: number): writer
- Alias of
writer:write_short.
writer:write_int(n: number): writer
writer:write_int(n: number): writer
- 4 bytes.
writer:write_uint(n: number): writer
writer:write_uint(n: number): writer
- Alias of
writer:write_int.
writer:write_int64(n: number): writer
writer:write_int64(n: number): writer
- 8 bytes.
writer:write_uint64(n: number): writer
writer:write_uint64(n: number): writer
- Alias of
writer:write_int64.
writer:write_float(n: number): writer
writer:write_float(n: number): writer
- IEEE 754, 4 bytes.
writer:write_double(n: number): writer
writer:write_double(n: number): writer
- IEEE 754, 8 bytes.
writer:write_varint(n: number): writer
writer:write_varint(n: number): writer
- Zigzag varint, 32 bits.
writer:write_varint64(n: number): writer
writer:write_varint64(n: number): writer
- Zigzag varint, 64 bits.
writer:write_varuint(n: number): writer
writer:write_varuint(n: number): writer
- Unsigned varint, 32 bits.
writer:write_varuint64(n: number): writer
writer:write_varuint64(n: number): writer
- Unsigned varint, 64 bits.
writer:write_string(s: string): writer
writer:write_string(s: string): writer
- Unsigned varint length, then the bytes.
writer:write_bytes(s: string): writer
writer:write_bytes(s: string): writer
- Raw bytes, no length.
writer:write_vec2(x: number, y: number): writer
writer:write_vec2(x: number, y: number): writer
- 2 floats; also takes a table with
x,y.
writer:write_vec3(x: number, y: number, z: number): writer
writer:write_vec3(x: number, y: number, z: number): writer
- 3 floats; also takes a table with
x,y,z.
writer:write_blockpos(x: number, y: number, z: number): writer
writer:write_blockpos(x: number, y: number, z: number): writer
- Zigzag varint x, unsigned varint y (negative y wraps), zigzag varint z.
writer:id(packet?: number | string): number
writer:id(packet?: number | string): number
- Gets or sets the packet id.
writer:size(): number
writer:size(): number
- Returns the payload length.
writer:data(): string
writer:data(): string
- Returns the payload string.
writer:clear()
writer:clear()
- Resets the payload to empty.
writer:send(): boolean, string
writer:send(): boolean, string
- Sends the packet; returns
trueorfalse, error. - The writer can be sent again.
Receiving
Receivers run once per frame, before on_frame, for every packet captured since the last frame. They see packets after the fact: they cannot change or cancel the packet. Up to 4096 packets are queued per frame. A receiver that raises an error is removed and the error is printed.
xiao.packet.receive(packet: number | string, identity: any, callback: function(msg: packet))
xiao.packet.receive(packet: number | string, identity: any, callback: function(msg: packet))
packetis an id, a name, or"*"for every packet.
xiao.packet.receive("Text", "chat_log", function(msg)
msg:read_byte()
msg:read_bool()
local source = msg:read_string()
print(msg.name, msg.outgoing and "sent" or "received", source)
end)
xiao.packet.receive("*", "counter", function(msg)
count = (count or 0) + 1
end)
xiao.packet.remove(packet: number | string, identity: any): boolean
xiao.packet.remove(packet: number | string, identity: any): boolean
- Returns
trueif the receiver existed.
xiao.packet.reader(bytes: string, packet?: number | string): packet
xiao.packet.reader(bytes: string, packet?: number | string): packet
- Packet object over any string, to parse saved payloads.
xiao.packet.decode(packet: number | string, bytes: string): string[] | nil
xiao.packet.decode(packet: number | string, bytes: string): string[] | nil
- Returns the lines of the built-in decoder (same as the packet log), or
nil.
xiao.packet.id(name: string): number | nil
xiao.packet.id(name: string): number | nil
- Returns the id or
nil.
xiao.packet.name(id: number): string | nil
xiao.packet.name(id: number): string | nil
- Returns the name or
nil.
xiao.packet.dropped(): number
xiao.packet.dropped(): number
- Returns the number of packets lost because the queue was full.
Packet object
Callbacks get a packet object, in both directions (sent by the game and received from the server).
packet
Reading past the end of the packet raises read past end of packet.
packet:read_bool(): boolean
packet:read_bool(): boolean
- Reads a boolean.
packet:read_byte(): number
packet:read_byte(): number
- Reads a byte.
packet:read_short(): number
packet:read_short(): number
- Reads a short.
packet:read_ushort(): number
packet:read_ushort(): number
- Reads an unsigned short.
packet:read_int(): number
packet:read_int(): number
- Reads an int.
packet:read_uint(): number
packet:read_uint(): number
- Reads an unsigned int.
packet:read_int64(): number
packet:read_int64(): number
- Reads a 64-bit int.
packet:read_uint64(): number
packet:read_uint64(): number
- Reads an unsigned 64-bit int.
packet:read_float(): number
packet:read_float(): number
- Reads a float.
packet:read_double(): number
packet:read_double(): number
- Reads a double.
packet:read_varint(): number
packet:read_varint(): number
- Reads a zigzag varint.
packet:read_varuint(): number
packet:read_varuint(): number
- Reads an unsigned varint.
packet:read_varint64(): number
packet:read_varint64(): number
- Reads a 64-bit zigzag varint.
packet:read_varuint64(): number
packet:read_varuint64(): number
- Reads a 64-bit unsigned varint.
packet:read_string(): string
packet:read_string(): string
- Reads a string.
packet:read_bytes(n?: number): string
packet:read_bytes(n?: number): string
- Reads
nbytes; withoutn, returns the rest of the payload.
packet:read_vec2(): vector2
packet:read_vec2(): vector2
- Reads 2 floats (8 bytes) and returns them as a
vector2withxandy.
packet:read_vec3(): vector3
packet:read_vec3(): vector3
- Reads 3 floats (12 bytes) and returns them as a
vector3withx,yandz, usable directly as a paint point.
packet:read_blockpos(): vector3
packet:read_blockpos(): vector3
- Reads a block position: zigzag varint x, unsigned varint y (wrapped to a signed 32-bit value), zigzag varint z.
- Returns a
vector3with integerx,y,z.
packet:tell(): number
packet:tell(): number
- Returns the read position.
packet:seek(pos: number)
packet:seek(pos: number)
- Sets the read position.
packet:remaining(): number
packet:remaining(): number
- Returns the number of bytes left to read.
packet:decode(): string[] | nil
packet:decode(): string[] | nil
- Decodes the packet with the built-in decoder.
Copy from the packet log
In the packet log, the menu of a packet has Copy as Lua. It copies code that sends the same packet again (shortened example):
xiao.packet.start("MovePlayer")
xiao.packet.write_varuint64(42)
xiao.packet.write_float(1)
xiao.packet.write_float(2)
xiao.packet.write_float(3)
xiao.packet.send()
- Each field the decoder knows becomes a typed
write_*call with its value, so it can be edited. - Bytes the decoder does not cover (unknown packet, NBT, value that a Lua number cannot hold exactly) become
write_bytes("\x.."). - The copied code always rebuilds the original payload byte for byte.
- The button is disabled when the log only kept part of the payload.
Blocking
The script block list adds to the packet log block list and is cleared on Clear VM.
xiao.packet.block(packet: number | string)
xiao.packet.block(packet: number | string)
- The client drops this packet id in both directions.
xiao.packet.unblock(packet: number | string)
xiao.packet.unblock(packet: number | string)
- Stops dropping it.
xiao.packet.unblock_all()
xiao.packet.unblock_all()
- Clears the script block list.
xiao.packet.is_blocked(packet: number | string): boolean
xiao.packet.is_blocked(packet: number | string): boolean
- Script block list only.
xiao.packet.block("Animate")
xiao.signal.add("unload", "restore", function() xiao.packet.unblock_all() end)
Limits
- A payload holds at most 512 bytes; writing past that raises
packet payload exceeds 512 bytes. - An integer outside its type range, or with a fraction, raises
integer out of range. - 64-bit values are Lua numbers: exact up to 2^53.
- The packet goes to the same connection, with the same reliability, as the last packet of that id the game sent.
- Up to 4096 packets are queued per frame for receivers.
sendqueues at most 8 pending sends.