Skip to main content

Packet

A buffer-based networking library. Instead of one RemoteEvent per message, you declare named packets with a fixed list of value types; every Fire is serialised into a shared buffer, and everything queued during a frame is flushed through a single RemoteEvent. This gives far smaller payloads than sending Lua tables, and makes the shape of every message explicit.

The module must live somewhere both sides can require (for example ReplicatedStorage): the server creates the RemoteEvent inside the module and the client waits for it.

Declaring packets

Packet.new(name, ...types) declares a packet. Declare the same packet with the same name and types on the server and on the client (a shared ModuleScript is the easiest way). Declaring a name twice returns the existing packet. The value types are read from the module itself, e.g. Packet.NumberU8, Packet.String, Packet.Vector3F32.

Plain Luau tables describe structured values:

  • { T } (a table holding exactly one type) is a variable-length array of T (up to 65535 elements, 2 bytes of length prefix).
  • Any other table, e.g. { Name = Packet.String, Level = Packet.NumberU8 } or { Packet.NumberU8, Packet.NumberU8 }, is a fixed table: every key is always sent, in sorted key order, with no per-field overhead. All keys must be of one kind (all strings or all numbers), and tables can be nested.

Value types

Numbers are not range-checked: writing a value outside the range of its type corrupts it, so pick the smallest type that fits.

Type Bytes Range / notes
NumberS8 1 -128 to 127
NumberS16 2 -32768 to 32767
NumberS24 3 -8388608 to 8388607
NumberS32 4 -2147483648 to 2147483647
NumberU8 1 0 to 255
NumberU16 2 0 to 65535
NumberU24 3 0 to 16777215
NumberU32 4 0 to 4294967295
NumberF16 2 Half float; integers exact up to ±2048, largest value ±65520
NumberF24 3 Custom float; integers exact up to ±262144, largest value ±4294959104
NumberF32 4 Single float; integers exact up to ±16777216, largest value about ±1.7e38
NumberF64 8 Double float; integers exact up to ±9007199254740992, no practical limit
NumberU4 1 {number, number}, two values from 0 to 15 packed in one byte
Boolean8 1 A single boolean
Boolean1 1 {boolean}, exactly eight booleans packed into one byte
BooleanNumber 1 {Boolean: boolean, Number: number}, the number from 0 to 127
String 1 + n string of up to 255 bytes
StringLong 2 + n string of up to 65535 bytes
Characters 1 + 6 bits/char string of up to 255 characters using only space, ., 0-9, A-Z, a-z (see Types/Characters.luau)
Buffer 1 + n buffer of up to 255 bytes
BufferLong 2 + n buffer of up to 65535 bytes
Instance 0 Instance, sent alongside the buffer; arrives as nil if the receiver cannot see it
Nil 0 Always nil
Any 1 + value Any supported value, with a 1-byte type tag: nil, number, string (255 bytes max), buffer (255 bytes max), Instance, boolean, NumberRange, BrickColor, Color3, UDim, UDim2, Rect, Vector2, Vector3, CFrame, Region3, NumberSequence, ColorSequence, EnumItem (listed enums only) and tables whose keys and values are any of these
NumberRange 8 Min and Max as F32
BrickColor 2 Any BrickColor
Color3 3 8 bits per channel
UDim 4 Scale to 3 decimal places (±32.767), Offset -32768 to 32767
UDim2 8 Two UDims as above
Rect 16 Min and Max as F32 pairs
Vector2S16 4 Components -32768 to 32767, integers only
Vector2F24 6 Components as F24
Vector2F32 8 Components as F32
Vector3S16 6 Components -32768 to 32767, integers only
Vector3F24 9 Components as F24
Vector3F32 12 Components as F32
CFrameF24U8 12 Position as F24, rotation as three 8-bit Euler angles (about 1.4 degree steps)
CFrameF32U8 15 Position as F32, rotation as three 8-bit Euler angles (about 1.4 degree steps)
CFrameF32U16 18 Position as F32, rotation as three 16-bit Euler angles (about 0.006 degree steps)
Region3 24 Min and Max corners as F32
NumberSequence 1 + 3n Up to 255 keypoints; Time, Value and Envelope quantised to 1/255 (0 to 1 only)
ColorSequence 1 + 4n Up to 255 keypoints; Time quantised to 1/255, colour 8 bits per channel
EnumItem 3 Any item of an Enum listed in Types/Enums.luau (add more there, 255 max)
Static1, Static2, Static3 1 A value from the matching Types/Static1.luau (etc.) list of up to 255 constants of any type; values not in the list arrive as nil

Events and requests

A packet without a response is an event: Fire on the client sends to the server and OnServerEvent fires there with the player first; Fire on the server broadcasts to every client and FireClient(player, ...) sends to one, firing OnClientEvent. Sends are batched and flushed once per frame, so Fire never yields for an event.

Calling :Response(...types) on a packet (on both sides) turns it into a request: the receiving side must set OnServerInvoke / OnClientInvoke, and Fire (client) or FireClient (server) yields until the reply arrives or ResponseTimeout seconds pass, then returns the reply or ResponseTimeoutValue. Request packets do not fire the event signals.

The server drops incoming data from a player that exceeds roughly 8 KB per frame (each received RemoteEvent call counts as at least 800 bytes), so keep client sends small.

Example

-- ReplicatedStorage/Packets.luau, required by both sides
local Packet = require(ReplicatedStorage.Packages.Packet)

return {
	-- client -> server: which hotbar slot was used and where the player aimed
	UseItem = Packet.new("UseItem", Packet.NumberU8, Packet.Vector3F32),
	-- server -> clients: an array of fixed tables
	Scores = Packet.new("Scores", {{Name = Packet.String, Score = Packet.NumberU16}}),
	-- client -> server request; the server answers with an array of strings
	GetInventory = Packet.new("GetInventory"):Response({Packet.String}),
}
-- Server
local Packets = require(ReplicatedStorage.Packets)

Packets.UseItem.OnServerEvent:Connect(function(player, slot, aimPosition)
	print(player.Name, "used slot", slot, "towards", aimPosition)
end)

Packets.GetInventory.OnServerInvoke = function(player)
	return {"Sword", "Shield"}
end

Packets.Scores:Fire({{Name = "Kash", Score = 10}, {Name = "Ana", Score = 7}})
-- Client
local Packets = require(ReplicatedStorage.Packets)

Packets.Scores.OnClientEvent:Connect(function(scores)
	for _, entry in scores do print(entry.Name, entry.Score) end
end)

Packets.UseItem:Fire(1, workspace.CurrentCamera.CFrame.LookVector * 100)

local inventory = Packets.GetInventory:Fire() -- yields until the server replies (10 s timeout)

Credits: Packet was created by Suphi Kaner (original release on the DevForum). This build is his library with a few quality-of-life changes by KashTheKing, published to Wally as kashtheking/packet so it can be installed like the rest of the library. The bundled Signal, Task and Types modules are his as well. All credit for the design and the serialization engine goes to Suphi.

Installation and guide: Packet package page.

Properties​

Name​

This item is read only and cannot be modified. Read Only
Packet.Name: string

The name the packet was declared with. Packets are cached by name, so declaring the same name again returns the same object.

Id​

This item is read only and cannot be modified. Read Only
Packet.Id: number

The one-byte id written at the start of every message for this packet. The server assigns it in declaration order and publishes it as an attribute on the RemoteEvent; on the client it is nil until the server has declared a packet with the same name.

ResponseTimeout​

Packet.ResponseTimeout: number

How many seconds Fire / FireClient wait for a reply on a request packet before giving up. Response sets it to 10 unless you assigned a value first; you can change it at any time.

ResponseTimeoutValue​

Packet.ResponseTimeoutValue: any

What Fire / FireClient return when a request times out. Defaults to nil.

OnServerEvent​

This item only works when running on the server. ServerThis item is read only and cannot be modified. Read Only
Packet.OnServerEvent: Signal<(
A...
)>

Fires on the server when a client Fires this event packet. Handlers receive the sending player followed by the packet's values. The signal has Connect, Once and Wait; Connect returns a connection with Disconnect. Not used by request packets.

OnClientEvent​

This item only works when running on the client. ClientThis item is read only and cannot be modified. Read Only
Packet.OnClientEvent: Signal<A...>

Fires on the client when the server Fires or FireClients this event packet. Handlers receive the packet's values. The signal has Connect, Once and Wait; Connect returns a connection with Disconnect. Not used by request packets.

OnServerInvoke​

This item only works when running on the server. Server
Packet.OnServerInvoke: ((
player: Player,
A...
) → B...)?

Callback the server must set on a request packet. It receives the requesting player and the packet's values and returns the response values. If it is nil when a request arrives the request is discarded (with a warning in Studio) and the client waits for its timeout.

OnClientInvoke​

This item only works when running on the client. Client
Packet.OnClientInvoke: ((A...) → B...)?

Callback the client must set on a request packet sent with FireClient. It receives the packet's values and returns the response values. If it is nil when a request arrives the request is discarded with a warning and the server waits for its timeout.

Functions​

new​

Packet.new(
name: string,--

A unique name for the packet, shared by both sides.

...: any--

The value types of the packet, in order (none for a packet without data).

) → Packet<A...,B...>--

The packet object.

Declares a packet, or returns the one already declared with that name. Declare it on both the server and the client with identical types. The types are the Packet.* value types listed in the class description, or Luau tables of them for arrays and fixed tables.

On the server this assigns the packet's Id and publishes it to clients; on the client the packet is inert until the server has declared the same name. This is the only way to declare a packet: the module table itself is not callable.

local Damage = Packet.new("Damage", Packet.Instance, Packet.NumberU16)
local Spawn = Packet.new("Spawn", {Position = Packet.Vector3F32, Skin = Packet.NumberU8})
local Ping = Packet.new("Ping"):Response(Packet.NumberF32)

newParams​

Packet.newParams(
...: any--

The value types to bundle, in order.

) → () → ...any--

A function that returns the bundled types.

Bundles a list of value types into a function that returns them, so the same parameter list can be reused across several packets or shared between a request and its response. Call the returned function inside Packet.new or Response to expand it.

local Position = Packet.newParams(Packet.NumberU8, Packet.Vector3F32)
local Move = Packet.new("Move", Position())
local Teleport = Packet.new("Teleport", Position()):Response(Position())

Response​

Packet:Response(
...: any--

The value types of the response, in order (see the class description).

) → Packet<A...,B...>--

The same packet, for chaining.

Declares the response types of the packet, turning it into a request/response packet. Call it on both sides, right after Packet.new, with the same syntax used for the packet's own types. Once a packet has a response, Fire (client) and FireClient (server) yield for the reply, the receiving side handles it through OnServerInvoke / OnClientInvoke, and the event signals are no longer fired.

Also sets ResponseTimeout to 10 if it has not been set yet.

local GetStats = Packet.new("GetStats", Packet.NumberU16):Response(Packet.NumberU32, Packet.String)

Fire​

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
Packet:Fire(
...: A...--

The values to send, matching the types given to Packet.new.

) → B...--

The response values (request packets only); nothing for event packets.

Sends the packet. Which way it goes depends on where it is called:

  • Client: sends to the server. For an event packet this returns immediately and OnServerEvent fires on the server. For a request packet it yields until the server's OnServerInvoke reply arrives and returns those values, or returns ResponseTimeoutValue after ResponseTimeout seconds.
  • Server: broadcasts an event packet to every connected client (OnClientEvent fires on each). Request packets cannot be broadcast; use FireClient instead.

Sends are queued and flushed once per frame in a single RemoteEvent call, so nothing is transmitted until the end of the current frame.

Errors

TypeDescription
"You must use FireClient(player)"When called on the server for a request packet.
"Cannot have more than 128 yielded threads"When 128 requests from this client are already waiting for a reply.

FireClient​

This item only works when running on the server. ServerThis is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
Packet:FireClient(
player: Player,--

The client to send to.

...: A...--

The values to send, matching the types given to Packet.new.

) → B...--

The response values (request packets only); nothing for event packets.

Sends the packet to one client. For an event packet this returns immediately and OnClientEvent fires on that client. For a request packet it yields until the client's OnClientInvoke reply arrives and returns those values, or returns ResponseTimeoutValue after ResponseTimeout seconds. If the player has already left the game nothing is sent and the call returns immediately with no values.

Like Fire, the data is queued and flushed at the end of the frame.

Errors

TypeDescription
"Cannot have more than 128 yielded threads"When 128 requests to this player are already waiting for a reply.

Serialize​

Packet:Serialize(
...: A...--

The values to encode, matching the types given to Packet.new.

) → (
buffer,--

The encoded bytes, sized exactly to the data.

{Instance}?--

The Instances that were referenced, in order; only returned when at least one Instance was written.

)

Encodes values with this packet's types into a standalone buffer without sending anything, using the same format as Fire. Useful for compact storage (for example in a DataStore) or for sending through your own remotes. Works on either side.

local SaveData = Packet.new("SaveData", Packet.NumberU32, {Packet.String})
local data = SaveData:Serialize(1500, {"Sword", "Shield"})
local coins, items = SaveData:Deserialize(data)

Deserialize​

Packet:Deserialize(
serializeBuffer: buffer,--

A buffer returned by Serialize.

instances: {Instance}?--

The Instance list returned by Serialize, if there was one.

) → A...--

The decoded values, in the order the packet declares them.

Decodes a buffer produced by Serialize on a packet with the same types, returning the original values. Works on either side.

Show raw api
{
    "functions": [
        {
            "name": "Response",
            "desc": "Declares the response types of the packet, turning it into a request/response packet.\nCall it on both sides, right after `Packet.new`, with the same syntax used for the packet's\nown types. Once a packet has a response, `Fire` (client) and `FireClient` (server) yield for\nthe reply, the receiving side handles it through `OnServerInvoke` / `OnClientInvoke`, and\nthe event signals are no longer fired.\n\nAlso sets `ResponseTimeout` to `10` if it has not been set yet.\n\n```lua\nlocal GetStats = Packet.new(\"GetStats\", Packet.NumberU16):Response(Packet.NumberU32, Packet.String)\n```",
            "params": [
                {
                    "name": "...",
                    "desc": "The value types of the response, in order (see the class description).",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "The same packet, for chaining.",
                    "lua_type": "Packet<A..., B...>"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 307,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "Fire",
            "desc": "Sends the packet. Which way it goes depends on where it is called:\n\n- **Client**: sends to the server. For an event packet this returns immediately and\n  `OnServerEvent` fires on the server. For a request packet it yields until the server's\n  `OnServerInvoke` reply arrives and returns those values, or returns `ResponseTimeoutValue`\n  after `ResponseTimeout` seconds.\n- **Server**: broadcasts an event packet to every connected client (`OnClientEvent` fires\n  on each). Request packets cannot be broadcast; use `FireClient` instead.\n\nSends are queued and flushed once per frame in a single RemoteEvent call, so nothing is\ntransmitted until the end of the current frame.",
            "params": [
                {
                    "name": "...",
                    "desc": "The values to send, matching the types given to `Packet.new`.",
                    "lua_type": "A..."
                }
            ],
            "returns": [
                {
                    "desc": "The response values (request packets only); nothing for event packets.",
                    "lua_type": "B..."
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"You must use FireClient(player)\"",
                    "desc": "When called on the server for a request packet."
                },
                {
                    "lua_type": "\"Cannot have more than 128 yielded threads\"",
                    "desc": "When 128 requests from this client are already waiting for a reply."
                }
            ],
            "yields": true,
            "source": {
                "line": 334,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "FireClient",
            "desc": "Sends the packet to one client. For an event packet this returns immediately and\n`OnClientEvent` fires on that client. For a request packet it yields until the client's\n`OnClientInvoke` reply arrives and returns those values, or returns `ResponseTimeoutValue`\nafter `ResponseTimeout` seconds. If the player has already left the game nothing is sent\nand the call returns immediately with no values.\n\nLike `Fire`, the data is queued and flushed at the end of the frame.",
            "params": [
                {
                    "name": "player",
                    "desc": "The client to send to.",
                    "lua_type": "Player"
                },
                {
                    "name": "...",
                    "desc": "The values to send, matching the types given to `Packet.new`.",
                    "lua_type": "A..."
                }
            ],
            "returns": [
                {
                    "desc": "The response values (request packets only); nothing for event packets.",
                    "lua_type": "B..."
                }
            ],
            "function_type": "method",
            "errors": [
                {
                    "lua_type": "\"Cannot have more than 128 yielded threads\"",
                    "desc": "When 128 requests to this player are already waiting for a reply."
                }
            ],
            "realm": [
                "Server"
            ],
            "yields": true,
            "source": {
                "line": 377,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "Serialize",
            "desc": "Encodes values with this packet's types into a standalone `buffer` without sending\nanything, using the same format as `Fire`. Useful for compact storage (for example in a\nDataStore) or for sending through your own remotes. Works on either side.\n\n```lua\nlocal SaveData = Packet.new(\"SaveData\", Packet.NumberU32, {Packet.String})\nlocal data = SaveData:Serialize(1500, {\"Sword\", \"Shield\"})\nlocal coins, items = SaveData:Deserialize(data)\n```",
            "params": [
                {
                    "name": "...",
                    "desc": "The values to encode, matching the types given to `Packet.new`.",
                    "lua_type": "A..."
                }
            ],
            "returns": [
                {
                    "desc": "The encoded bytes, sized exactly to the data.",
                    "lua_type": "buffer"
                },
                {
                    "desc": "The Instances that were referenced, in order; only returned when at least one `Instance` was written.",
                    "lua_type": "{Instance}?"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 421,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "Deserialize",
            "desc": "Decodes a buffer produced by `Serialize` on a packet with the same types, returning the\noriginal values. Works on either side.",
            "params": [
                {
                    "name": "serializeBuffer",
                    "desc": "A buffer returned by `Serialize`.",
                    "lua_type": "buffer"
                },
                {
                    "name": "instances",
                    "desc": "The Instance list returned by `Serialize`, if there was one.",
                    "lua_type": "{Instance}?"
                }
            ],
            "returns": [
                {
                    "desc": "The decoded values, in the order the packet declares them.",
                    "lua_type": "A..."
                }
            ],
            "function_type": "method",
            "source": {
                "line": 437,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "new",
            "desc": "Declares a packet, or returns the one already declared with that name. Declare it on both\nthe server and the client with identical types. The types are the `Packet.*` value types\nlisted in the class description, or Luau tables of them for arrays and fixed tables.\n\nOn the server this assigns the packet's `Id` and publishes it to clients; on the client the\npacket is inert until the server has declared the same name. This is the only way to\ndeclare a packet: the module table itself is not callable.\n\n```lua\nlocal Damage = Packet.new(\"Damage\", Packet.Instance, Packet.NumberU16)\nlocal Spawn = Packet.new(\"Spawn\", {Position = Packet.Vector3F32, Skin = Packet.NumberU8})\nlocal Ping = Packet.new(\"Ping\"):Response(Packet.NumberF32)\n```",
            "params": [
                {
                    "name": "name",
                    "desc": "A unique name for the packet, shared by both sides.",
                    "lua_type": "string"
                },
                {
                    "name": "...",
                    "desc": "The value types of the packet, in order (none for a packet without data).",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "The packet object.",
                    "lua_type": "Packet<A..., B...>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 704,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "newParams",
            "desc": "Bundles a list of value types into a function that returns them, so the same parameter\nlist can be reused across several packets or shared between a request and its response.\nCall the returned function inside `Packet.new` or `Response` to expand it.\n\n```lua\nlocal Position = Packet.newParams(Packet.NumberU8, Packet.Vector3F32)\nlocal Move = Packet.new(\"Move\", Position())\nlocal Teleport = Packet.new(\"Teleport\", Position()):Response(Position())\n```",
            "params": [
                {
                    "name": "...",
                    "desc": "The value types to bundle, in order.",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "A function that returns the bundled types.",
                    "lua_type": "() -> ...any"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 724,
                "path": "packages/src/Packet/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "Name",
            "desc": "The name the packet was declared with. Packets are cached by name, so declaring the same\nname again returns the same object.",
            "lua_type": "string",
            "readonly": true,
            "source": {
                "line": 227,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "Id",
            "desc": "The one-byte id written at the start of every message for this packet. The server assigns\nit in declaration order and publishes it as an attribute on the RemoteEvent; on the client\nit is `nil` until the server has declared a packet with the same name.",
            "lua_type": "number",
            "readonly": true,
            "source": {
                "line": 236,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "ResponseTimeout",
            "desc": "How many seconds `Fire` / `FireClient` wait for a reply on a request packet before giving\nup. `Response` sets it to `10` unless you assigned a value first; you can change it at any\ntime.",
            "lua_type": "number",
            "source": {
                "line": 244,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "ResponseTimeoutValue",
            "desc": "What `Fire` / `FireClient` return when a request times out. Defaults to `nil`.",
            "lua_type": "any",
            "source": {
                "line": 250,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "OnServerEvent",
            "desc": "Fires on the server when a client `Fire`s this event packet. Handlers receive the sending\nplayer followed by the packet's values. The signal has `Connect`, `Once` and `Wait`;\n`Connect` returns a connection with `Disconnect`. Not used by request packets.",
            "lua_type": "Signal<(Player, A...)>",
            "realm": [
                "Server"
            ],
            "readonly": true,
            "source": {
                "line": 260,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "OnClientEvent",
            "desc": "Fires on the client when the server `Fire`s or `FireClient`s this event packet. Handlers\nreceive the packet's values. The signal has `Connect`, `Once` and `Wait`; `Connect` returns\na connection with `Disconnect`. Not used by request packets.",
            "lua_type": "Signal<A...>",
            "realm": [
                "Client"
            ],
            "readonly": true,
            "source": {
                "line": 270,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "OnServerInvoke",
            "desc": "Callback the server must set on a request packet. It receives the requesting player and\nthe packet's values and returns the response values. If it is `nil` when a request arrives\nthe request is discarded (with a warning in Studio) and the client waits for its timeout.",
            "lua_type": "((player: Player, A...) -> B...)?",
            "realm": [
                "Server"
            ],
            "source": {
                "line": 279,
                "path": "packages/src/Packet/init.luau"
            }
        },
        {
            "name": "OnClientInvoke",
            "desc": "Callback the client must set on a request packet sent with `FireClient`. It receives the\npacket's values and returns the response values. If it is `nil` when a request arrives the\nrequest is discarded with a warning and the server waits for its timeout.",
            "lua_type": "((A...) -> B...)?",
            "realm": [
                "Client"
            ],
            "source": {
                "line": 288,
                "path": "packages/src/Packet/init.luau"
            }
        }
    ],
    "types": [],
    "name": "Packet",
    "desc": "A buffer-based networking library. Instead of one RemoteEvent per message, you declare\nnamed packets with a fixed list of value types; every `Fire` is serialised into a shared\n`buffer`, and everything queued during a frame is flushed through a single RemoteEvent.\nThis gives far smaller payloads than sending Lua tables, and makes the shape of every\nmessage explicit.\n\nThe module must live somewhere both sides can `require` (for example ReplicatedStorage):\nthe server creates the RemoteEvent inside the module and the client waits for it.\n\n## Declaring packets\n\n`Packet.new(name, ...types)` declares a packet. Declare the same packet with the same name\nand types on the server and on the client (a shared ModuleScript is the easiest way).\nDeclaring a name twice returns the existing packet. The value types are read from the\nmodule itself, e.g. `Packet.NumberU8`, `Packet.String`, `Packet.Vector3F32`.\n\nPlain Luau tables describe structured values:\n\n- `{ T }` (a table holding exactly one type) is a variable-length **array** of `T`\n  (up to 65535 elements, 2 bytes of length prefix).\n- Any other table, e.g. `{ Name = Packet.String, Level = Packet.NumberU8 }` or\n  `{ Packet.NumberU8, Packet.NumberU8 }`, is a **fixed table**: every key is always sent,\n  in sorted key order, with no per-field overhead. All keys must be of one kind (all\n  strings or all numbers), and tables can be nested.\n\n## Value types\n\nNumbers are not range-checked: writing a value outside the range of its type corrupts it,\nso pick the smallest type that fits.\n\n| Type | Bytes | Range / notes |\n|------|-------|---------------|\n| `NumberS8` | 1 | -128 to 127 |\n| `NumberS16` | 2 | -32768 to 32767 |\n| `NumberS24` | 3 | -8388608 to 8388607 |\n| `NumberS32` | 4 | -2147483648 to 2147483647 |\n| `NumberU8` | 1 | 0 to 255 |\n| `NumberU16` | 2 | 0 to 65535 |\n| `NumberU24` | 3 | 0 to 16777215 |\n| `NumberU32` | 4 | 0 to 4294967295 |\n| `NumberF16` | 2 | Half float; integers exact up to ±2048, largest value ±65520 |\n| `NumberF24` | 3 | Custom float; integers exact up to ±262144, largest value ±4294959104 |\n| `NumberF32` | 4 | Single float; integers exact up to ±16777216, largest value about ±1.7e38 |\n| `NumberF64` | 8 | Double float; integers exact up to ±9007199254740992, no practical limit |\n| `NumberU4` | 1 | `{number, number}`, two values from 0 to 15 packed in one byte |\n| `Boolean8` | 1 | A single `boolean` |\n| `Boolean1` | 1 | `{boolean}`, exactly eight booleans packed into one byte |\n| `BooleanNumber` | 1 | `{Boolean: boolean, Number: number}`, the number from 0 to 127 |\n| `String` | 1 + n | `string` of up to 255 bytes |\n| `StringLong` | 2 + n | `string` of up to 65535 bytes |\n| `Characters` | 1 + 6 bits/char | `string` of up to 255 characters using only space, `.`, `0-9`, `A-Z`, `a-z` (see `Types/Characters.luau`) |\n| `Buffer` | 1 + n | `buffer` of up to 255 bytes |\n| `BufferLong` | 2 + n | `buffer` of up to 65535 bytes |\n| `Instance` | 0 | `Instance`, sent alongside the buffer; arrives as `nil` if the receiver cannot see it |\n| `Nil` | 0 | Always `nil` |\n| `Any` | 1 + value | Any supported value, with a 1-byte type tag: `nil`, `number`, `string` (255 bytes max), `buffer` (255 bytes max), `Instance`, `boolean`, `NumberRange`, `BrickColor`, `Color3`, `UDim`, `UDim2`, `Rect`, `Vector2`, `Vector3`, `CFrame`, `Region3`, `NumberSequence`, `ColorSequence`, `EnumItem` (listed enums only) and tables whose keys and values are any of these |\n| `NumberRange` | 8 | Min and Max as F32 |\n| `BrickColor` | 2 | Any BrickColor |\n| `Color3` | 3 | 8 bits per channel |\n| `UDim` | 4 | Scale to 3 decimal places (±32.767), Offset -32768 to 32767 |\n| `UDim2` | 8 | Two `UDim`s as above |\n| `Rect` | 16 | Min and Max as F32 pairs |\n| `Vector2S16` | 4 | Components -32768 to 32767, integers only |\n| `Vector2F24` | 6 | Components as F24 |\n| `Vector2F32` | 8 | Components as F32 |\n| `Vector3S16` | 6 | Components -32768 to 32767, integers only |\n| `Vector3F24` | 9 | Components as F24 |\n| `Vector3F32` | 12 | Components as F32 |\n| `CFrameF24U8` | 12 | Position as F24, rotation as three 8-bit Euler angles (about 1.4 degree steps) |\n| `CFrameF32U8` | 15 | Position as F32, rotation as three 8-bit Euler angles (about 1.4 degree steps) |\n| `CFrameF32U16` | 18 | Position as F32, rotation as three 16-bit Euler angles (about 0.006 degree steps) |\n| `Region3` | 24 | Min and Max corners as F32 |\n| `NumberSequence` | 1 + 3n | Up to 255 keypoints; Time, Value and Envelope quantised to 1/255 (0 to 1 only) |\n| `ColorSequence` | 1 + 4n | Up to 255 keypoints; Time quantised to 1/255, colour 8 bits per channel |\n| `EnumItem` | 3 | Any item of an Enum listed in `Types/Enums.luau` (add more there, 255 max) |\n| `Static1`, `Static2`, `Static3` | 1 | A value from the matching `Types/Static1.luau` (etc.) list of up to 255 constants of any type; values not in the list arrive as `nil` |\n\n## Events and requests\n\nA packet without a response is an **event**: `Fire` on the client sends to the server and\n`OnServerEvent` fires there with the player first; `Fire` on the server broadcasts to every\nclient and `FireClient(player, ...)` sends to one, firing `OnClientEvent`. Sends are\nbatched and flushed once per frame, so `Fire` never yields for an event.\n\nCalling `:Response(...types)` on a packet (on both sides) turns it into a **request**: the\nreceiving side must set `OnServerInvoke` / `OnClientInvoke`, and `Fire` (client) or\n`FireClient` (server) yields until the reply arrives or `ResponseTimeout` seconds pass,\nthen returns the reply or `ResponseTimeoutValue`. Request packets do not fire the event\nsignals.\n\nThe server drops incoming data from a player that exceeds roughly 8 KB per frame (each\nreceived RemoteEvent call counts as at least 800 bytes), so keep client sends small.\n\n## Example\n\n```lua\n-- ReplicatedStorage/Packets.luau, required by both sides\nlocal Packet = require(ReplicatedStorage.Packages.Packet)\n\nreturn {\n\t-- client -> server: which hotbar slot was used and where the player aimed\n\tUseItem = Packet.new(\"UseItem\", Packet.NumberU8, Packet.Vector3F32),\n\t-- server -> clients: an array of fixed tables\n\tScores = Packet.new(\"Scores\", {{Name = Packet.String, Score = Packet.NumberU16}}),\n\t-- client -> server request; the server answers with an array of strings\n\tGetInventory = Packet.new(\"GetInventory\"):Response({Packet.String}),\n}\n```\n\n```lua\n-- Server\nlocal Packets = require(ReplicatedStorage.Packets)\n\nPackets.UseItem.OnServerEvent:Connect(function(player, slot, aimPosition)\n\tprint(player.Name, \"used slot\", slot, \"towards\", aimPosition)\nend)\n\nPackets.GetInventory.OnServerInvoke = function(player)\n\treturn {\"Sword\", \"Shield\"}\nend\n\nPackets.Scores:Fire({{Name = \"Kash\", Score = 10}, {Name = \"Ana\", Score = 7}})\n```\n\n```lua\n-- Client\nlocal Packets = require(ReplicatedStorage.Packets)\n\nPackets.Scores.OnClientEvent:Connect(function(scores)\n\tfor _, entry in scores do print(entry.Name, entry.Score) end\nend)\n\nPackets.UseItem:Fire(1, workspace.CurrentCamera.CFrame.LookVector * 100)\n\nlocal inventory = Packets.GetInventory:Fire() -- yields until the server replies (10 s timeout)\n```\n\n**Credits:** Packet was created by **[Suphi Kaner](https://devforum.roblox.com/u/5uphi)** ([original release on the DevForum](https://devforum.roblox.com/t/packet-networking-library/3573907)). This build is his library with a few quality-of-life changes by KashTheKing, published to Wally as `kashtheking/packet` so it can be installed like the rest of the library. The bundled `Signal`, `Task` and `Types` modules are his as well. All credit for the design and the serialization engine goes to Suphi.\n\nInstallation and guide: [Packet package page](/docs/packages/packet).",
    "source": {
        "line": 183,
        "path": "packages/src/Packet/init.luau"
    }
}