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 = {via: string,teleportData: {[string]: any}?,instanceId: string?,spawnName: string?}
A teleport recorded against a PlayerMock. via names the TeleportService API the caller reached
for.
TeleportConfig
type TeleportConfig = {teleportData: {[string]: any}?,maxAttempts: number?,retryWait: number?,exponential: number?,printWarning: boolean?,initFailedGraceSeconds: number?}
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
TeleportConfig under the name it had while only a client retried.
Functions
teleport
This item only works when running on the client. ClientTeleportServiceUtils.teleport(placeId: number,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(placeId: number,instanceId: string,--
the destination job/server id
spawnName: string?,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
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
Wraps TeleportService:ReserveServer(placeId)
promiseTeleportServerOnce
This item only works when running on the server. Server
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. ClientTeleportServiceUtils.promiseTeleportClientOnce(placeId: number,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(placeId: number,configOrOptions: TeleportConfig | 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. ClientThis was deprecated in 10.3.0
TeleportServiceUtils.promiseTeleportClient(placeId: number,config: TeleportClientConfig?) → 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.