Packet log New
In the packet log, the menu of a packet has Copy as Lua and Copy as receiver. They write a script for that exact packet to the clipboard, with readable names taken from the schemas of the game.
Both are generated off the render thread and announced by a notification when the code is in the clipboard (COPIED AS LUA, COPIED AS RECEIVER, or COPY FAILED).
Limits
- The buttons are disabled while a copy runs and when the log only kept part of the payload.
- Payloads over 4 MiB are refused (
PACKET TOO LARGE). - Sending still needs a payload of 512 bytes or less (see Limits).
Copy as Lua
Copies code that sends the same packet again. The values come first, named after the decoder fields, and the write_* calls use them. Generated from a MovePlayer packet:
local MODE = { normal = 0, respawn = 1, teleport = 2, only_head_rot = 3 }
local runtime_id = 1
local position = { x = -70.5, y = 64.62, z = 8.4 }
local pitch = 0
local yaw = 90
local head_yaw = 90
local mode = MODE.normal
local on_ground = true
local ridden_runtime_id = 0
local has_teleport = false
local tick = 1234
xiao.packet.start("MovePlayer")
xiao.packet.write_varuint64(runtime_id)
xiao.packet.write_vec3(position.x, position.y, position.z)
xiao.packet.write_float(pitch)
xiao.packet.write_float(yaw)
xiao.packet.write_float(head_yaw)
xiao.packet.write_byte(mode)
xiao.packet.write_bool(on_ground)
xiao.packet.write_varuint64(ridden_runtime_id)
xiao.packet.write_bool(has_teleport)
xiao.packet.write_varuint64(tick)
xiao.packet.send()
What the generator does:
- Enums get a table of their names (
MODE) and the value uses it (MODE.normal); a value without a unique name stays a number. An enum of up to 16 values lists every name, a larger one only the names the packet uses. - Vectors are tables written with
write_vec2,write_vec3orwrite_blockpos. - Lists and sub-structures are nested tables:
attribute[1].maximum,item.id,request[1].action[1].count. - Bytes the decoder does not cover (unknown packet, end of a partial decode) become named strings,
local raw_1 = "\x.."thenwrite_bytes(raw_1); runs over 256 bytes stay inlinewrite_bytes("\x..")lines of 32 bytes. - NBT, UUIDs, skins and values a Lua number cannot hold exactly are named strings written with
write_bytes; a length written just before such a string is#name(write_varuint(#item.extra)), or its length field when the string is empty (write_varuint(item.extra_length)). - Presence flags and counts use their field (
write_bool(has_teleport),write_varuint(links)): a decoded packet has no literalwrite_*argument. - Past 150 values and enum tables, the values go in one table
local p = { ... }(p.runtime_id) to stay under the Lua limit of 200 locals. - The copied code always rebuilds the original payload byte for byte; if a value cannot be written back exactly, the whole payload is copied as
write_byteslines instead.
Edit the values at the top, not the write_* calls. To change a few values and keep the rest, a send_fields script is shorter.
Copy as receiver
Copies a receiver that reads the same packet field by field, in payload order, then prints the first 6 fields. Generated from the same packet:
xiao.packet.receive("MovePlayer", "move_player_copy", function(msg)
local runtime_id = msg:read_varuint64()
local position = msg:read_vec3()
local pitch = msg:read_float()
local yaw = msg:read_float()
local head_yaw = msg:read_float()
local mode = msg:read_byte()
local on_ground = msg:read_bool()
local ridden_runtime_id = msg:read_varuint64()
local has_teleport = msg:read_bool()
local tick = msg:read_varuint64()
print("MovePlayer", "runtime_id", runtime_id, "position", position, "pitch", pitch, "yaw", yaw, "head_yaw", head_yaw, "mode", mode)
end)
- Names are the field paths with
_:attribute_1_name,item_extra. - Bytes the decoder does not cover, NBT and other raw values are read with
msg:read_bytes(n). - On the source packet the receiver reads exactly the whole payload. Lists and optional fields are read as they were in that packet: another packet of the same id with a different count needs a loop, or
msg:fields(). - Past 150 fields, the values go in
local p = {}(p.attribute_32_name = msg:read_string()).
Workflow
- Capture the packet in the packet log (do the action in game).
- Open its menu and pick Copy as Lua or Copy as receiver.
- Paste in the Lua tab, adjust values, press Execute.