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(raw: any) → booleanWhether 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(sharedSlice: TeleportDataSlice?,--
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(raw: any,--
the raw arrived teleport data
userId: number | 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(trustedRaw: any,--
raw arrived data trusted for this player (server-authored), or nil
nonTrustedRaw: any,--
raw arrived data asserted by the client, or nil
userId: number | 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(data: any) → numberApproximate 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(data: any) → SizeClassification
Classifies teleport data against the size budget: ok, warn (approaching the cap), or over
(past it -- will likely be dropped/error by Roblox).