Skip to main content

TeleportServiceUtils

Utilities for teleporting players, including mock-aware wrappers of the TeleportService teleport APIs. A PlayerMock records its teleports in the "TeleportService.Teleport" lookup domain, keyed by destination placeId, instead of reaching the engine:

TeleportServiceUtils.teleport(placeId, mock, { SlotId = "abc" })
local hop = PlayerMock.readLookup(mock, "TeleportService.Teleport", placeId)

What the engine would have said back goes in through the "TeleportService.TeleportInitFailed" lookup the same way, result and all:

PlayerMock.writeLookup(mock, "TeleportService.TeleportInitFailed", placeId, {
	result = Enum.TeleportResult.GameFull,
	message = "server full",
})

Types

MockTeleport

type MockTeleport = {
viastring,
teleportData{[string]any}?,
instanceIdstring?,
spawnNamestring?
}

A teleport recorded against a PlayerMock. via names the TeleportService API the caller reached for.

TeleportConfig

type TeleportConfig = {
teleportData{[string]any}?,
teleportOptionsTeleportOptions?,
maxAttemptsnumber?,
retryWaitnumber?,
exponentialnumber?,
printWarningboolean?,
initFailedGraceSecondsnumber?
}

What a teleport carries and how it retries when the engine refuses it (see TeleportServiceUtils.promiseTeleport). maxAttempts = 1 means no retry at all, and exponential is the per-attempt backoff multiplier (1 keeps the wait flat).

teleportOptions is the server's extra reach -- reserved servers, a named spawn -- and a server call may pass one on its own instead of a config. A client has only teleportData, which is read back out of the options when a call passes those instead.

initFailedGraceSeconds is how long a server attempt keeps listening for a refusal the engine raises after it has already accepted the request (see TeleportServiceUtils._promiseTeleportServerAttempt). Unset -- the default -- resolves on acceptance, which is what every server call did before this existed.

TeleportClientConfig

type TeleportClientConfig = TeleportConfig

TeleportConfig under the name it had while only a client retried.

Functions

teleport

This item only works when running on the client. Client
TeleportServiceUtils.teleport(
placeIdnumber,
playerPlayer,--

the local player, or a PlayerMock standing in for one

teleportData{[string]any}?
) → (
boolean,--

Whether the teleport was started.

string?--

Why the engine refused it, when it did.

)

Mock-aware TeleportService:Teleport(placeId, player, teleportData), yielded out of TeleportServiceUtils.promiseTeleport -- so it retries like every other teleport now, and answers whether the player is going anywhere rather than only whether the call was made.

A client teleport that works ends with the player gone, so on the happy path this never returns: there is nothing left to return to. It comes back only when the teleport has failed, which is what makes the answer worth having.

teleportToPlaceInstance

TeleportServiceUtils.teleportToPlaceInstance(
placeIdnumber,
instanceIdstring,--

the destination job/server id

playerPlayer,
spawnNamestring?,
teleportData{[string]any}?
) → ()

Mock-aware TeleportService:TeleportToPlaceInstance(placeId, instanceId, player, spawnName, teleportData), which sends a player to one specific running server (e.g. joining a friend).

teleportAsync

This item only works when running on the server. Server
TeleportServiceUtils.teleportAsync(
placeIdnumber,
players{Player},
teleportOptionsTeleportOptions?
) → TeleportAsyncResult?

Mock-aware TeleportService:TeleportAsync(placeId, players, teleportOptions), yielded out of TeleportServiceUtils.promiseTeleport -- so it retries like every other teleport now, and throws what the last attempt threw. Mock players in the batch are recorded (with the options' teleport data) and dropped from the engine call; a batch of only mocks skips the engine and returns nil.

promiseReserveServer

TeleportServiceUtils.promiseReserveServer(placeIdnumber) → Promise<string>--

Code

Wraps TeleportService:ReserveServer(placeId)

promiseTeleportServerOnce

This item only works when running on the server. Server
TeleportServiceUtils.promiseTeleportServerOnce(
placeIdnumber,
players{Player},
teleportOptionsTeleportOptions?
) → Promise<TeleportAsyncResult?>

One server teleport request, and only ever one: a promise wrapper of TeleportService:TeleportAsync -- mock-aware too, recording mock players and resolving without an engine call when the batch is all mocks. Retrying is TeleportServiceUtils.promiseTeleport's job.

Unlike the client's request this one settles: TeleportAsync yields until the engine has accepted the teleport and throws when it has not, so the promise says which happened.

promiseTeleportClientOnce

This item only works when running on the client. Client
TeleportServiceUtils.promiseTeleportClientOnce(
placeIdnumber,
playerPlayer,--

the local player, or a PlayerMock standing in for one

teleportData{[string]any}?
) → Promise<()>--

Pending while in flight; rejects with a TeleportFailedReportUtils.TeleportReport when refused.

One client teleport request, and only ever one. Issues a single TeleportService:Teleport and then reports what the engine says about it.

Roblox cannot hold more than one outstanding request per player -- a second earns Enum.TeleportResult.IsTeleporting, and there is no API to cancel the first -- so this never asks twice. Retrying is TeleportServiceUtils.promiseTeleportClient's job, and it may only ask again once this has rejected, which is the only evidence the request is gone.

The promise reports failure alone: a teleport that works ends with the player leaving, so success stays pending until the client is gone. It rejects with the whole TeleportFailedReportUtils.TeleportReport rather than a message, so a caller can decide on result. Destroying it stops listening and rejects with nothing, letting a caller tell its own teardown from a refusal.

maid._teleport = TeleportServiceUtils.promiseTeleportClientOnce(placeId, Players.LocalPlayer, data)
	:Catch(function(report)
		if report then
			print(report.result, report.message)
		end
	end)

promiseTeleport

TeleportServiceUtils.promiseTeleport(
placeIdnumber,
playersPlayer | {Player},--

one player or a batch; a client sends only itself

configOrOptionsTeleportConfig | TeleportOptions | nil--

a bare TeleportOptions reads as a config carrying it

) → Promise<TeleportAsyncResult?>--

Resolves on a server; on a client, pending until the player is gone.

A teleport, whichever realm asks for it, retried while a refusal is worth asking about. The realm picks the request: a server batches through TeleportServiceUtils.promiseTeleportServerOnce, a client sends itself through TeleportServiceUtils.promiseTeleportClientOnce.

One request is outstanding at a time: the next only goes out after the last has settled, which on the client is the only evidence Roblox gives that a request is gone. So a refusal the engine will only repeat (TeleportFailedReportUtils.shouldRetry) rejects at once rather than spending the budget, and a report that means the hop is still running never starts a second one.

Where it settles differs because the realms differ, not because the shape does. A server teleport resolves with its TeleportAsyncResult -- the server is still here afterwards. A client teleporting itself reports only failure: a teleport that works ends with the player gone, so it stays pending until they are. Destroying it stops the retries and rejects with nothing either way, so a caller can tell its own teardown from a hop that genuinely failed.

A server teleport resolves as soon as the engine accepts the request, which is not the same as the player going anywhere: a refusal raised afterwards (Unauthorized, most of all) would otherwise never reach the caller at all. config.initFailedGraceSeconds holds each attempt open that long to catch one -- see TeleportServiceUtils._promiseTeleportServerAttempt. Worth setting wherever something is waiting on the hop, because without it a refused teleport is indistinguishable from one still on its way.

Rejects with the TeleportFailedReportUtils.TeleportReport itself when a refusal ends it, and with the retry wrapper's summary once the attempts are spent -- a report renders itself, so either reads as text.

-- Server: a batch, and the options every call passed before this was unified still work
TeleportServiceUtils.promiseTeleport(placeId, { player }, teleportOptions)

-- Client: itself
maid._teleport = TeleportServiceUtils.promiseTeleport(placeId, Players.LocalPlayer, {
	teleportData = teleportData,
})

promiseTeleportClient

This item only works when running on the client. Client
deprecated in 10.3.0
</>
This was deprecated in 10.3.0
Use [TeleportServiceUtils.promiseTeleport], which does this in either realm.
TeleportServiceUtils.promiseTeleportClient(
placeIdnumber,
playerPlayer,--

the local player, or a PlayerMock standing in for one

) → Promise<()>--

Pending while in flight; rejects once the player is going nowhere.

TeleportServiceUtils.promiseTeleport under the name a client used before either realm shared it.

Show raw api
{
    "functions": [
        {
            "name": "teleport",
            "desc": "Mock-aware `TeleportService:Teleport(placeId, player, teleportData)`, yielded out of\n[TeleportServiceUtils.promiseTeleport] -- so it retries like every other teleport now, and answers\nwhether the player is going anywhere rather than only whether the call was made.\n\nA client teleport that works ends with the player gone, so on the happy path this never returns:\nthere is nothing left to return to. It comes back only when the teleport has failed, which is what\nmakes the answer worth having.",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "player",
                    "desc": "the local player, or a PlayerMock standing in for one",
                    "lua_type": "Player"
                },
                {
                    "name": "teleportData",
                    "desc": "",
                    "lua_type": "{ [string]: any }?"
                }
            ],
            "returns": [
                {
                    "desc": "Whether the teleport was started.",
                    "lua_type": "boolean"
                },
                {
                    "desc": "Why the engine refused it, when it did.",
                    "lua_type": "string?"
                }
            ],
            "function_type": "static",
            "realm": [
                "Client"
            ],
            "source": {
                "line": 114,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "teleportToPlaceInstance",
            "desc": "Mock-aware `TeleportService:TeleportToPlaceInstance(placeId, instanceId, player, spawnName, teleportData)`,\nwhich sends a player to one specific running server (e.g. joining a friend).",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "instanceId",
                    "desc": "the destination job/server id",
                    "lua_type": "string"
                },
                {
                    "name": "player",
                    "desc": "",
                    "lua_type": "Player"
                },
                {
                    "name": "spawnName",
                    "desc": "",
                    "lua_type": "string?"
                },
                {
                    "name": "teleportData",
                    "desc": "",
                    "lua_type": "{ [string]: any }?"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 138,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "teleportAsync",
            "desc": "Mock-aware `TeleportService:TeleportAsync(placeId, players, teleportOptions)`, yielded out of\n[TeleportServiceUtils.promiseTeleport] -- so it retries like every other teleport now, and throws\nwhat the last attempt threw. Mock players in the batch are recorded (with the options' teleport\ndata) and dropped from the engine call; a batch of only mocks skips the engine and returns nil.",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "teleportOptions",
                    "desc": "",
                    "lua_type": "TeleportOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TeleportAsyncResult?"
                }
            ],
            "function_type": "static",
            "realm": [
                "Server"
            ],
            "source": {
                "line": 174,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "promiseReserveServer",
            "desc": "Wraps TeleportService:ReserveServer(placeId)",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "Code",
                    "lua_type": "Promise<string>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 194,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "promiseTeleportServerOnce",
            "desc": "One server teleport request, and only ever one: a promise wrapper of `TeleportService:TeleportAsync`\n-- mock-aware too, recording mock players and resolving without an engine call when the batch is all\nmocks. Retrying is [TeleportServiceUtils.promiseTeleport]'s job.\n\nUnlike the client's request this one settles: TeleportAsync yields until the engine has accepted the\nteleport and throws when it has not, so the promise says which happened.",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "players",
                    "desc": "",
                    "lua_type": "{ Player }"
                },
                {
                    "name": "teleportOptions",
                    "desc": "",
                    "lua_type": "TeleportOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise<TeleportAsyncResult?>"
                }
            ],
            "function_type": "static",
            "realm": [
                "Server"
            ],
            "source": {
                "line": 224,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "promiseTeleportClientOnce",
            "desc": "One client teleport request, and only ever one. Issues a single `TeleportService:Teleport` and then\nreports what the engine says about it.\n\nRoblox cannot hold more than one outstanding request per player -- a second earns\n`Enum.TeleportResult.IsTeleporting`, and there is no API to cancel the first -- so this never asks\ntwice. Retrying is [TeleportServiceUtils.promiseTeleportClient]'s job, and it may only ask again\nonce this has rejected, which is the only evidence the request is gone.\n\nThe promise reports failure alone: a teleport that works ends with the player leaving, so success\nstays pending until the client is gone. It rejects with the whole [TeleportFailedReportUtils.TeleportReport] rather than a\nmessage, so a caller can decide on `result`. Destroying it stops listening and rejects with\nnothing, letting a caller tell its own teardown from a refusal.\n\n```lua\nmaid._teleport = TeleportServiceUtils.promiseTeleportClientOnce(placeId, Players.LocalPlayer, data)\n\t:Catch(function(report)\n\t\tif report then\n\t\t\tprint(report.result, report.message)\n\t\tend\n\tend)\n```",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "player",
                    "desc": "the local player, or a PlayerMock standing in for one",
                    "lua_type": "Player"
                },
                {
                    "name": "teleportData",
                    "desc": "",
                    "lua_type": "{ [string]: any }?"
                }
            ],
            "returns": [
                {
                    "desc": "Pending while in flight; rejects with a TeleportFailedReportUtils.TeleportReport when refused.",
                    "lua_type": "Promise<()>"
                }
            ],
            "function_type": "static",
            "realm": [
                "Client"
            ],
            "source": {
                "line": 278,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "promiseTeleport",
            "desc": "A teleport, whichever realm asks for it, retried while a refusal is worth asking about. The realm\npicks the request: a server batches through [TeleportServiceUtils.promiseTeleportServerOnce], a\nclient sends itself through [TeleportServiceUtils.promiseTeleportClientOnce].\n\nOne request is outstanding at a time: the next only goes out after the last has settled, which on\nthe client is the only evidence Roblox gives that a request is gone. So a refusal the engine will\nonly repeat ([TeleportFailedReportUtils.shouldRetry]) rejects at once rather than spending the\nbudget, and a report that means the hop is still running never starts a second one.\n\nWhere it settles differs because the realms differ, not because the shape does. A server teleport\nresolves with its `TeleportAsyncResult` -- the server is still here afterwards. A client teleporting\nitself reports only failure: a teleport that works ends with the player gone, so it stays pending\nuntil they are. Destroying it stops the retries and rejects with nothing either way, so a caller can\ntell its own teardown from a hop that genuinely failed.\n\nA server teleport resolves as soon as the engine accepts the request, which is not the same as the\nplayer going anywhere: a refusal raised afterwards (`Unauthorized`, most of all) would otherwise\nnever reach the caller at all. `config.initFailedGraceSeconds` holds each attempt open that long to\ncatch one -- see [TeleportServiceUtils._promiseTeleportServerAttempt]. Worth setting wherever\nsomething is waiting on the hop, because without it a refused teleport is indistinguishable from one\nstill on its way.\n\nRejects with the [TeleportFailedReportUtils.TeleportReport] itself when a refusal ends it, and with\nthe retry wrapper's summary once the attempts are spent -- a report renders itself, so either reads\nas text.\n\n```lua\n-- Server: a batch, and the options every call passed before this was unified still work\nTeleportServiceUtils.promiseTeleport(placeId, { player }, teleportOptions)\n\n-- Client: itself\nmaid._teleport = TeleportServiceUtils.promiseTeleport(placeId, Players.LocalPlayer, {\n\tteleportData = teleportData,\n})\n```",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "players",
                    "desc": "one player or a batch; a client sends only itself",
                    "lua_type": "Player | { Player }"
                },
                {
                    "name": "configOrOptions",
                    "desc": "a bare TeleportOptions reads as a config carrying it",
                    "lua_type": "TeleportConfig | TeleportOptions | nil"
                }
            ],
            "returns": [
                {
                    "desc": "Resolves on a server; on a client, pending until the player is gone.",
                    "lua_type": "Promise<TeleportAsyncResult?>"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 361,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "promiseTeleportClient",
            "desc": "[TeleportServiceUtils.promiseTeleport] under the name a client used before either realm shared it.",
            "params": [
                {
                    "name": "placeId",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "player",
                    "desc": "the local player, or a PlayerMock standing in for one",
                    "lua_type": "Player"
                },
                {
                    "name": "config",
                    "desc": "",
                    "lua_type": "TeleportClientConfig?"
                }
            ],
            "returns": [
                {
                    "desc": "Pending while in flight; rejects once the player is going nowhere.",
                    "lua_type": "Promise<()>"
                }
            ],
            "function_type": "static",
            "realm": [
                "Client"
            ],
            "deprecated": {
                "version": "10.3.0",
                "desc": "Use [TeleportServiceUtils.promiseTeleport], which does this in either realm."
            },
            "source": {
                "line": 426,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "MockTeleport",
            "desc": "A teleport recorded against a [PlayerMock]. `via` names the TeleportService API the caller reached\nfor.",
            "lua_type": "{ via: string, teleportData: { [string]: any }?, instanceId: string?, spawnName: string? }",
            "source": {
                "line": 56,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "TeleportConfig",
            "desc": "What a teleport carries and how it retries when the engine refuses it (see\n[TeleportServiceUtils.promiseTeleport]). `maxAttempts = 1` means no retry at all, and `exponential`\nis the per-attempt backoff multiplier (1 keeps the wait flat).\n\n`teleportOptions` is the server's extra reach -- reserved servers, a named spawn -- and a server\ncall may pass one on its own instead of a config. A client has only `teleportData`, which is read\nback out of the options when a call passes those instead.\n\n`initFailedGraceSeconds` is how long a *server* attempt keeps listening for a refusal the engine\nraises after it has already accepted the request (see\n[TeleportServiceUtils._promiseTeleportServerAttempt]). Unset -- the default -- resolves on acceptance,\nwhich is what every server call did before this existed.",
            "lua_type": "{ teleportData: { [string]: any }?, teleportOptions: TeleportOptions?, maxAttempts: number?, retryWait: number?, exponential: number?, printWarning: boolean?, initFailedGraceSeconds: number? }",
            "source": {
                "line": 80,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        },
        {
            "name": "TeleportClientConfig",
            "desc": "[TeleportConfig] under the name it had while only a client retried.",
            "lua_type": "TeleportConfig",
            "source": {
                "line": 96,
                "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
            }
        }
    ],
    "name": "TeleportServiceUtils",
    "desc": "Utilities for teleporting players, including mock-aware wrappers of the TeleportService teleport\nAPIs. A [PlayerMock] records its teleports in the `\"TeleportService.Teleport\"` lookup domain, keyed\nby destination placeId, instead of reaching the engine:\n\n```lua\nTeleportServiceUtils.teleport(placeId, mock, { SlotId = \"abc\" })\nlocal hop = PlayerMock.readLookup(mock, \"TeleportService.Teleport\", placeId)\n```\n\nWhat the engine would have said back goes in through the `\"TeleportService.TeleportInitFailed\"`\nlookup the same way, `result` and all:\n\n```lua\nPlayerMock.writeLookup(mock, \"TeleportService.TeleportInitFailed\", placeId, {\n\tresult = Enum.TeleportResult.GameFull,\n\tmessage = \"server full\",\n})\n```",
    "source": {
        "line": 24,
        "path": "src/teleportserviceutils/src/Shared/TeleportServiceUtils.lua"
    }
}