In Packet
On this page
The in_packet object represents incoming packet data. It comes in two flavors:
- Read-only view — passed to
on_packet_recv/on_packet_sendcallbacks. Use it to decode bytes from a real packet the game is processing. - Builder — created with
in_packet(opcode). Build a synthetic incoming packet, then call:inject()to have the game process it as if the server had sent it.
out_packet(...):send()— sends a packet to the server (outgoing).in_packet(...):inject()— feeds a packet into the game's incoming dispatch path (loopback). The server never sees this packet — it's processed locally as if received.- Both share the same encoding methods.
Read Methods
These methods are available on packets passed to on_packet_recv / on_packet_send.
packet:get_opcode() -> number
Returns the packet opcode.
function on_packet_recv(packet)
local opcode = packet:get_opcode()
core.log(string.format("Opcode: 0x%04X", opcode))
return true
endpacket:decode_1() -> number
Reads 1 byte (0-255).
packet:decode_2() -> number
Reads 2 bytes (0-65535).
packet:decode_4() -> number
Reads 4 bytes (0-4294967295).
packet:decode_8() -> integer
Reads 8 bytes as an unsigned 64-bit value, returned as a Luau integer (int64) to preserve the full bit pattern. This is a distinct numeric type from regular number — see Luau Notes › 64-bit packet integers for comparison/arithmetic caveats.
packet:decode_string() -> string
Reads a length-prefixed string.
packet:decode_buffer(size: number) -> buffer | nil
Reads size bytes and returns them as a native Luau buffer. Returns nil if not enough data remains.
Access bytes through the buffer library rather than table indexing:
local buf = packet:decode_buffer(16)
for i = 0, buffer.len(buf) - 1 do
local byte = buffer.readu8(buf, i) -- 0-indexed, unlike table access
core.log(string.format("byte[%d] = 0x%02X", i, byte))
endbuffer is mutable, byte-addressable, and avoids the allocation cost of a Lua table per byte. Pass it straight to out_packet:encode_buffer to forward the bytes untouched.
packet:get_length() -> number
Returns the total packet length in bytes.
packet:get_offset() -> number
Returns the current read offset position.
packet:set_offset(offset: number)
Sets the read offset to a specific position. Allows rewinding to re-read data.
Decode methods maintain an internal offset. Each read advances the position. Use set_offset() to rewind if you need to re-read data.
Read Example
function on_packet_recv(packet)
local opcode = packet:get_opcode()
if opcode == 0x1234 then -- Replace with actual opcode
local player_id = packet:decode_4()
local x_pos = packet:decode_2()
local y_pos = packet:decode_2()
core.log(string.format("Player %d at (%d, %d)",
player_id, x_pos, y_pos))
end
return true
endConstructor (Injection)
in_packet(opcode: number) -> InPacketBuilder
Creates a new packet for injection. The opcode is written automatically; chain encode_* calls to fill the body, then call :inject().
local pkt = in_packet(0x100)Injecting malformed packets can crash the client or trigger server-side detection on subsequent legit traffic. Only inject opcodes whose layout you've verified by reading real captured packets first.
Encoding Methods
All methods return the builder so calls can be chained.
builder:encode_1(value: number) -> InPacketBuilder
Encodes 1 byte (0-255).
builder:encode_2(value: number) -> InPacketBuilder
Encodes 2 bytes (0-65535).
builder:encode_4(value: number) -> InPacketBuilder
Encodes 4 bytes (0-4294967295).
builder:encode_8(value: number) -> InPacketBuilder
Encodes 8 bytes.
builder:encode_string(text: string) -> InPacketBuilder
Encodes a length-prefixed string.
builder:encode_buffer(data: string | buffer) -> InPacketBuilder
Encodes raw binary data. Accepts either a Lua string or a Luau buffer.
Injection Methods
builder:inject()
Queues the packet to be dispatched as if received from the server. Processed on the next main tick, so it's safe to call from inside on_packet_recv without re-entrancy issues. Injected packets are flagged internally as loopback.
builder:to_string() -> string
Returns the hex representation of the packet contents (debug helper).
builder:get_opcode() -> number
Returns the packet's opcode.
builder:get_data() -> string
Alias of to_string().
Injection Example
-- Loop back a synthetic chat message
in_packet(0x00A2)
:encode_1(0) -- channel
:encode_string("hi") -- message
:inject()-- Method chaining + buffer forwarding from a real incoming packet
function on_packet_recv(packet)
if packet:get_opcode() == 0x1234 then
local body = packet:decode_buffer(packet:get_length() - 4)
in_packet(0x5678):encode_buffer(body):inject()
end
return true
end