Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Elitriare/ByteNet-Max/llms.txt

Use this file to discover all available pages before exploring further.

ByteNet Max provides four composite data types that let you group, list, and conditionally include values in a single packet. Each composite wraps one or more primitive (or other composite) types and handles the framing bytes needed to delimit the data on the wire. Choosing the right composite keeps your packets both expressive and compact.

struct

Signature: ByteNetMax.struct({ field = dataType, ... }) A struct serializes a fixed-shape Lua table where every field name and its corresponding type are declared up front. Fields are written in a consistent order determined on the server side and automatically replicated to clients, so both ends always agree on the layout without extra negotiation traffic.
local ByteNetMax = require(path.to.ByteNetMax)

return ByteNetMax.defineNamespace("Main", function()
    return {
        packets = {
            Test = ByteNetMax.definePacket({
                value = ByteNetMax.struct({
                    Action = ByteNetMax.string,
                    Data   = ByteNetMax.string,
                }),
            }),
        },
    }
end)
On the receiving end, the deserialized value is a plain Lua table with the same field names:
BytePackets.packets.Test.listen(function(data, player)
    print(data.Action) -- e.g. "Jump"
    print(data.Data)   -- e.g. "extraPayload"
end)
Field order inside a struct is determined server-side at namespace initialization and automatically synchronized to all clients. You do not need to manually coordinate ordering — just declare the fields and ByteNet Max handles the rest.
Prefer struct over map for fixed-shape data. Because struct encodes field identities at initialization rather than inline, it avoids writing key strings into the packet buffer on every send, making it significantly more efficient for data whose shape you know ahead of time.
When to use: Any time your packet has a known, stable set of named fields — player state, game events, configuration payloads.

array

Signature: ByteNetMax.array(dataType) An array serializes an ordered Lua sequence (a table with consecutive integer keys starting at 1). The length is prefixed as a uint16 (2 bytes), followed by each element serialized using the provided dataType.
local ByteNetMax = require(path.to.ByteNetMax)

return ByteNetMax.defineNamespace("Scores", function()
    return {
        packets = {
            -- Send a list of up to 65,535 byte-sized values
            TopScores = ByteNetMax.definePacket({
                value = ByteNetMax.array(ByteNetMax.uint8),
            }),
        },
    }
end)
Elements can be any data type, including nested composites:
-- Array of structs
ByteNetMax.array(ByteNetMax.struct({
    playerId = ByteNetMax.uint8,
    score    = ByteNetMax.uint32,
}))
When to use: Ordered lists of homogeneous items (leaderboard entries, inventory slots, projectile positions). The element type must be uniform — use auto or unknown if you need a mixed list.

map

Signature: ByteNetMax.map(keyType, valueType) A map serializes an unordered Lua dictionary. Like array, it prefixes the entry count as a uint16, then writes alternating key/value pairs using the provided types.
local ByteNetMax = require(path.to.ByteNetMax)

return ByteNetMax.defineNamespace("Inventory", function()
    return {
        packets = {
            -- item name -> quantity mapping
            InventorySync = ByteNetMax.definePacket({
                value = ByteNetMax.map(ByteNetMax.string, ByteNetMax.uint32),
            }),
        },
    }
end)
Both the key and value types can be any primitive or composite:
-- Player ID (uint8) -> team name (string)
ByteNetMax.map(ByteNetMax.uint8, ByteNetMax.string)
When to use: Associative data where the set of keys is not known at definition time (dynamic inventories, arbitrary metadata). For data with a fixed key set, struct is more efficient because it avoids serializing the key strings on every send.

optional

Signature: ByteNetMax.optional(dataType) An optional wraps any other type to allow it to be nil. It prefixes the value with a 1-byte presence flag: 0 means absent (only 1 byte total on the wire), 1 means present (1 byte + the serialized inner value).
local ByteNetMax = require(path.to.ByteNetMax)

return ByteNetMax.defineNamespace("Chat", function()
    return {
        packets = {
            Message = ByteNetMax.definePacket({
                value = ByteNetMax.struct({
                    text    = ByteNetMax.string,
                    -- reply target is optional
                    replyTo = ByteNetMax.optional(ByteNetMax.string),
                }),
            }),
        },
    }
end)
When reading, the deserialized value is either the inner type’s value or nil:
ChatPackets.packets.Message.listen(function(data)
    print(data.text)
    if data.replyTo then
        print("Replying to:", data.replyTo)
    end
end)
When to use: Any field that is legitimately absent in some messages. Wrapping with optional is always preferable to sending a sentinel value (like an empty string or -1) to represent “no value”.

Build docs developers (and LLMs) love