Skip to main content

TeleportDataEnvelopeUtils

Pure assembly and reading of the per-player teleport-data envelope, plus the size guard.

A group teleport carries a single [TeleportData] table that every arriving player reads back through GetJoinData().TeleportData. To carry per-player data we wrap the contributions in an envelope: shared keys under one reserved key, and each player's slice under another keyed by stringified UserId (the only identity stable across a teleport). Each player merges the shared slice with their own on arrival. A non-envelope table (an in-flight teleport from a build predating this, or a hand-written TeleportData) is read as-is, so the change is backward compatible.

All logic here is pure and keyed by plain numbers, so it is unit tested without any Player. It is shared (not server-only) because both realms unwrap arrived data: the server keys each player's slice by their UserId, the client by the local player's.

Functions

isEnvelope

TeleportDataEnvelopeUtils.isEnvelope(rawany) → boolean

Whether a table read from teleport data is one of our envelopes (versus a legacy/hand-written flat table, which carries data keys at the root).

build

TeleportDataEnvelopeUtils.build(
sharedSliceTeleportDataSlice?,--

applied to every arriving player

perPlayerByUserId{[string]TeleportDataSlice}?--

keyed by stringified UserId

) → {[string]any}

Assembles an envelope from a shared slice and a map of per-player slices keyed by UserId. Empty sections are omitted, so a teleport carrying nothing serializes to {}.

readSlice

TeleportDataEnvelopeUtils.readSlice(
rawany,--

the raw arrived teleport data

userIdnumber | string--

the arriving player's UserId

) → TeleportDataSlice?

Reads the slice a specific player arrived with: the shared slice merged with that player's own (per-player wins on conflict, being the more specific). A non-envelope table is returned as-is (legacy/hand-written flat data). Returns nil when the player carried nothing.

readMergedSlice

TeleportDataEnvelopeUtils.readMergedSlice(
trustedRawany,--

raw arrived data trusted for this player (server-authored), or nil

nonTrustedRawany,--

raw arrived data asserted by the client, or nil

userIdnumber | string--

the arriving player's UserId

) → TeleportDataSlice?

Reads a player's slice from two raw arrived tables of different trust and merges them, with the trusted band winning on conflict. This is the single place the "trusted over non-trusted" precedence lives, so both realms compute the identical unified view.

A teleport carries the trusted band (server-authored, from the server's join data) in one raw table and the non-trusted band (client-authored, from the client's local teleport data) in the other; either may be nil. Returns nil when neither band carried anything for the player.

measureBytes

TeleportDataEnvelopeUtils.measureBytes(dataany) → number

Approximate serialized byte size of teleport data, via its JSON encoding. Encoding also fails loudly on genuinely un-encodable data (a cyclic table, a function value), so that surfaces here at build time rather than as a broken teleport.

classifySize

TeleportDataEnvelopeUtils.classifySize(dataany) → SizeClassification

Classifies teleport data against the size budget: ok, warn (approaching the cap), or over (past it -- will likely be dropped/error by Roblox).

Show raw api
{
    "functions": [
        {
            "name": "isEnvelope",
            "desc": "Whether a table read from teleport data is one of our envelopes (versus a legacy/hand-written flat\ntable, which carries data keys at the root).",
            "params": [
                {
                    "name": "raw",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 49,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        },
        {
            "name": "build",
            "desc": "Assembles an envelope from a shared slice and a map of per-player slices keyed by UserId. Empty\nsections are omitted, so a teleport carrying nothing serializes to `{}`.",
            "params": [
                {
                    "name": "sharedSlice",
                    "desc": "applied to every arriving player",
                    "lua_type": "TeleportDataSlice?"
                },
                {
                    "name": "perPlayerByUserId",
                    "desc": "keyed by stringified UserId",
                    "lua_type": "{ [string]: TeleportDataSlice }?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ [string]: any }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 65,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        },
        {
            "name": "readSlice",
            "desc": "Reads the slice a specific player arrived with: the shared slice merged with that player's own\n(per-player wins on conflict, being the more specific). A non-envelope table is returned as-is\n(legacy/hand-written flat data). Returns nil when the player carried nothing.",
            "params": [
                {
                    "name": "raw",
                    "desc": "the raw arrived teleport data",
                    "lua_type": "any"
                },
                {
                    "name": "userId",
                    "desc": "the arriving player's UserId",
                    "lua_type": "number | string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TeleportDataSlice?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 91,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        },
        {
            "name": "readMergedSlice",
            "desc": "Reads a player's slice from two raw arrived tables of different trust and merges them, with the\ntrusted band winning on conflict. This is the single place the \"trusted over non-trusted\" precedence\nlives, so both realms compute the identical unified view.\n\nA teleport carries the trusted band (server-authored, from the server's join data) in one raw table\nand the non-trusted band (client-authored, from the client's local teleport data) in the other;\neither may be nil. Returns nil when neither band carried anything for the player.",
            "params": [
                {
                    "name": "trustedRaw",
                    "desc": "raw arrived data trusted for this player (server-authored), or nil",
                    "lua_type": "any"
                },
                {
                    "name": "nonTrustedRaw",
                    "desc": "raw arrived data asserted by the client, or nil",
                    "lua_type": "any"
                },
                {
                    "name": "userId",
                    "desc": "the arriving player's UserId",
                    "lua_type": "number | string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TeleportDataSlice?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 140,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        },
        {
            "name": "measureBytes",
            "desc": "Approximate serialized byte size of teleport data, via its JSON encoding. Encoding also fails\nloudly on genuinely un-encodable data (a cyclic table, a function value), so that surfaces here at\nbuild time rather than as a broken teleport.",
            "params": [
                {
                    "name": "data",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 177,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        },
        {
            "name": "classifySize",
            "desc": "Classifies teleport data against the size budget: `ok`, `warn` (approaching the cap), or `over`\n(past it -- will likely be dropped/error by Roblox).",
            "params": [
                {
                    "name": "data",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "SizeClassification"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 196,
                "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
            }
        }
    ],
    "properties": [],
    "types": [],
    "name": "TeleportDataEnvelopeUtils",
    "desc": "Pure assembly and reading of the per-player teleport-data envelope, plus the size guard.\n\nA group teleport carries a single [TeleportData] table that every arriving player reads back\nthrough `GetJoinData().TeleportData`. To carry *per-player* data we wrap the contributions in an\nenvelope: shared keys under one reserved key, and each player's slice under another keyed by\nstringified `UserId` (the only identity stable across a teleport). Each player merges the shared\nslice with their own on arrival. A non-envelope table (an in-flight teleport from a build predating\nthis, or a hand-written `TeleportData`) is read as-is, so the change is backward compatible.\n\nAll logic here is pure and keyed by plain numbers, so it is unit tested without any [Player]. It is\nshared (not server-only) because both realms unwrap arrived data: the server keys each player's\nslice by their UserId, the client by the local player's.",
    "source": {
        "line": 18,
        "path": "src/teleportserviceutils/src/Shared/TeleportDataEnvelopeUtils.lua"
    }
}