AgentStack
SKILL verified MIT Self-run

Roblox Teleport

skill-nonlooped-roblox-suite-roblox-teleport · by nonlooped

Roblox TeleportService and multi-place architecture — the modern TeleportAsync API that replaces deprecated Teleport/TeleportPartyAsync/TeleportToPrivateServer variants. Covers TeleportOptions, reserved servers via ReserveServerAsync, the 50-player group limit, cross-experience restrictions, teleport data (and its client-spoofable security caveat), custom loading screens, DataStore handoff, and m…

No reviews yet
0 installs
9 views
0.0% view→install

Install

$ agentstack add skill-nonlooped-roblox-suite-roblox-teleport

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Roblox Teleport? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

roblox-teleport

Official sources (always check these for the latest):

  • https://create.roblox.com/docs/en-us/reference/engine/classes/TeleportService
  • https://create.roblox.com/docs/en-us/projects/teleport
  • https://create.roblox.com/docs/en-us/reference/engine/classes/TeleportOptions
  • https://create.roblox.com/docs/en-us/reference/engine/classes/TeleportAsyncResult
  • https://create.roblox.com/docs/projects/teleport (multi-place architecture)

TeleportService moves players between places and servers. The modern entry point is TeleportAsync; the older Teleport/TeleportPartyAsync/TeleportToPlaceInstance/TeleportToPrivateServer/TeleportToSpawnByName methods are deprecated and should not be used for new work.

Cross-reference:

  • [roblox-datastores/SKILL.md](../roblox-datastores/SKILL.md) — handoff pattern for player data across teleports.
  • [roblox-networking/SKILL.md](../roblox-networking/SKILL.md) — server authority, rate limiting, and the security model around teleport requests.
  • [roblox-core/SKILL.md](../roblox-core/SKILL.md) — services and script contexts.

When to use this skill

Activate when:

  • Moving players between places in a universe (lobby → game world → dungeon).
  • Building reserved/private servers (VIP servers, instanced dungeons, matchmaking rooms).
  • Implementing matchmaking (server browser, party join, follow-friend).
  • Passing data across teleports (lobby selection → game server).
  • Building a custom teleport loading screen.
  • Debugging teleport failures (TeleportInitFailed).

Multi-place architecture

A universe (experience) can contain multiple places. Each place has its own PlaceId; the universe has a UniverseId (exposed as game.GameId). One place is the primary place — the one players join from the Roblox app's main entry point.

  • Create additional places in the Creator Dashboard under your experience → Places.
  • Each place must be published and (for it to be joinable) visible/enabled.
  • game.PlaceId is the current place; game.GameId is the universe ID (sometimes called UniverseId).
  • Places in the same universe share DataStores, MemoryStores, and Asset permissions.
  • Cross-universe (cross-experience) teleports are restricted by default — see "Cross-experience teleports" below.

TeleportAsync (the modern method)

TeleportService:TeleportAsync(placeId, players, teleportOptions?) is the single unified method. It can teleport to a different place, a specific server, or a reserved server. Server-only — calling from the client errors.

--!strict
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")

local DESTINATION_PLACE_ID = 1234567890

local function teleportToPlace(player: Player)
    local options = Instance.new("TeleportOptions")
    -- options.ServerInstanceId = "..." -- to target a specific server
    -- options.ReservedServerAccessCode = "..." -- to join a reserved server
    -- options.ShouldReserveServer = true -- to create+join a new reserved server
    options:SetTeleportData({ reason = "lobby_choice", mode = "classic" })

    local ok, result = pcall(function()
        return TeleportService:TeleportAsync(DESTINATION_PLACE_ID, { player }, options)
    end)
    if not ok then
        warn("TeleportAsync failed:", tostring(result))
        return false
    end
    -- result is a TeleportAsyncResult with info about the destination
    return true
end

Group teleport limits

  • Groups can only be teleported within a single experience (same universe).
  • Max 50 players per TeleportAsync call.

Conflicting TeleportOptions

These combinations error with "Incompatible Parameters":

  • ReservedServerAccessCode + ServerInstanceId
  • ShouldReserveServer + ServerInstanceId
  • ShouldReserveServer + ReservedServerAccessCode

TeleportOptions

| Property | Purpose | | --- | --- | | ServerInstanceId | Target a specific server by JobId. | | ReservedServerAccessCode | Join an existing reserved server by its access code. | | ShouldReserveServer | Create a new reserved server and teleport into it. | | SetTeleportData(data) / GetTeleportData() | Pass data to the destination (retrieved via GetLocalPlayerTeleportData). |

TeleportAsyncResult

When TeleportOptions is passed, TeleportAsync returns a TeleportAsyncResult with information about the final teleport destination. Useful for confirming where a player ended up.

TeleportInitFailed (error handling)

TeleportService.TeleportInitFailed fires when a teleport fails to start. Connect to it to show a retry UI. Failure reasons include invalid place ID, empty player list, non-Player instances, wrong teleport options type, client-called TeleportAsync, and conflicting parameters.

TeleportService.TeleportInitFailed:Connect(function(player: Player, options: TeleportOptions, reason: Enum.TeleportResult, errMsg: string, placeId: number, groupName: string)
    warn(`Teleport failed for {player.Name}: {reason} - {errMsg}`)
    -- Show retry UI, log, etc.
end)

Reserved servers

TeleportService:ReserveServerAsync(placeId) (server-only, yields) returns an access code plus a PrivateServerId. Players can only join a reserved server via that access code — they can't join through normal matchmaking.

  • A server starts when the access code is first used.
  • Access codes remain valid indefinitely; a reserved server can be rejoined even if no server is currently running (a new one starts).
  • Detect reserved-server context: local isReserved = game.PrivateServerId ~= "" and game.PrivateServerOwnerId == 0.
  • PrivateServerId is constant across server instances for the same access code; JobId is not.
  • Cross-platform caveat: Xbox/PlayStation players with cross-play disabled land in a different server than cross-play-enabled players — multiple servers can share a PrivateServerId. Use game.MatchmakingType to differentiate.
--!strict
local TeleportService = game:GetService("TeleportService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

-- Reserve once, persist the code, reuse it
local store = DataStoreService:GetDataStore("ReservedServerCodes")
local code
local ok, saved = pcall(function() return store:GetAsync("DungeonRoom_1") end)
if ok and typeof(saved) == "string" then
    code = saved
else
    local reserveOk, newCode = pcall(function() return TeleportService:ReserveServerAsync(game.PlaceId) end)
    if reserveOk then
        code = newCode
        pcall(function() store:SetAsync("DungeonRoom_1", code) end)
    end
end

local function sendToDungeon(player: Player)
    if not code then return end
    local options = Instance.new("TeleportOptions")
    options.ReservedServerAccessCode = code
    options:SetTeleportData({ room = 1 })
    pcall(function()
        TeleportService:TeleportAsync(game.PlaceId, { player }, options)
    end)
end

Passing data across teleports

Two mechanisms, with different security profiles:

TeleportOptions:SetTeleportData / GetLocalPlayerTeleportData

Set on the source server (via TeleportOptions), retrieved on the destination client via TeleportService:GetLocalPlayerTeleportData(). Client-only retrieval. Can carry any serializable value.

Security: exploiters can spoof teleport data. Send sensitive data (currency, entitlements) through DataStoreService instead, and use teleport data only for non-sensitive hints (selected game mode, cosmetic loadout preference).

SetTeleportSetting / GetTeleportSetting

Client-only, stored locally, preserved across teleports within the same game. Good for client-side preferences (crouch state, UI tab) that don't need server trust. Also spoofable — use server-side validation for anything that affects gameplay.

Custom loading screen

TeleportService:SetTeleportGui(gui) (client) sets a ScreenGui shown during teleport. TeleportService:GetArrivingTeleportGui() (client, at the destination) retrieves it so you can parent it to PlayerGui and run your own transition. The teleport GUI does not cross into a different game (universe), and does not persist across multiple teleports — set it before each one.

--!strict
-- At the destination, in ReplicatedFirst
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")
local ReplicatedFirst = game:GetService("ReplicatedFirst")

local customLoadingScreen = TeleportService:GetArrivingTeleportGui()
if customLoadingScreen then
    local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
    ReplicatedFirst:RemoveDefaultLoadingScreen()
    customLoadingScreen.Parent = playerGui
    task.wait(5)
    customLoadingScreen:Destroy()
end

Cross-experience teleports

Teleporting from your experience to another experience owned by others fails by default. To enable cross-experience teleportation, follow the steps in the official Teleport between places doc. Within your own universe (cross-place), teleports work without extra setup.

TeleportService:PromptExperienceDetailsAsync(player, universeId) (client-only, yields) prompts the player with experience details and a Join button. If the player is ineligible to join the target experience, the button is disabled. The join button is always disabled during Studio playtesting.

Matchmaking patterns

Pattern 1: Lobby → match (simple round-robin)

A lobby place collects players; when enough are ready, TeleportAsync sends them to a game place. Use MessagingService to coordinate across lobby servers if you have multiple lobbies.

Pattern 2: Reserved server per match

For instanced dungeons or private matches: ReserveServerAsync → persist the access code in a DataStore keyed by match ID → teleport the party with ReservedServerAccessCode → players arrive in an isolated server.

Pattern 3: Server browser via MemoryStoreService

Maintain a MemoryStoreService sorted map of active servers (keyed by JobId, value = player count / mode / ping). The lobby lists entries; when a player picks one, TeleportOptions.ServerInstanceId = jobId and TeleportAsync. Update the sorted map from each game server on join/leave. This scales better than MessagingService for many servers.

Pattern 4: External matchmaking service

For complex matchmaking (skill-based, party queues), use an external HTTP service as the matchmaker. It returns a placeId + JobId or a fresh reserved-server access code; the lobby then TeleportAsyncs the party. See roblox-networking for the HttpService security model and roblox-open-cloud for in-experience Open Cloud calls.

All matchmaking must respect the 50-player-per-TeleportAsync limit; split larger groups into multiple calls.

DataStore handoff across teleports

The robust pattern for moving player state between places:

  1. Source server: on the triggering event, save the player's profile via UpdateAsync (see roblox-datastores). Then teleport.
  2. Destination server: on PlayerAdded, load the profile via GetAsync (consider UseCache=false for a fresh read if the save was recent).
  3. Race condition: if the save hasn't flushed by the time the destination loads, you'll get stale data. Mitigate by: (a) awaiting save confirmation before teleporting when the data is critical, or (b) passing a "load this version" hint in teleport data and retrying the load until it matches.
  4. Never trust teleport data for the authoritative state. Teleport data is a hint; the DataStore is the truth.

Security

  • TeleportAsync is server-only. The client cannot call it directly — if you need client-initiated teleports, the client sends a RemoteEvent and the server validates then calls TeleportAsync.
  • Rate-limit teleport requests per player. A hostile client spamming "teleport me" Remotes can disrupt your server flow and burn DataStore budget if you save on each trigger.
  • Validate the destination. Don't let a client-supplied place ID drive the teleport unsanitized — whitelist allowed destinations server-side.
  • Teleport data is spoofable. Treat it as a hint; re-validate any gameplay-affecting claim on the destination server against DataStores.
  • GetLocalPlayerTeleportData is client-only and returns client-controlled data. Don't use it for authoritative decisions on the server.

Studio limitation

TeleportService does not work during Studio playtesting. You must publish the experience and test teleports in the Roblox application. Plan for this in your testing workflow — teleport logic can only be verified live.

Gotchas

  • Players lose character state across teleports (new place = new character). Save/load via DataStores; don't try to carry live character state.
  • BindToClose on the source server can race with the teleport — finish saves before triggering teleports when data is critical.
  • The default teleport loading message was removed; CustomizedTeleportUI is deprecated and does nothing.
  • Cross-platform (Xbox/PS with cross-play off) can split a reserved server into multiple servers with the same PrivateServerId.
  • Group teleports are universe-only; you cannot TeleportAsync a group across experiences.
  • The 50-player limit is per call — larger groups need multiple calls, and ordering matters for "arrive together" semantics.
  • Follow-friend: Players.PlayerAdded → check player.FollowUserIdGetPlayerPlaceInstanceAsync(followId)TeleportToPlaceInstance. Note GetPlayerPlaceInstanceAsync returns JobId, not the reserved-server access code, so it won't work to follow a friend into a reserved server.

Scripts

  • scripts/TeleportHelper.lua — a server-side helper that wraps TeleportAsync with pcall, options, and a simple reserved-server-code cache.

How to proceed

  1. Decide your place architecture: primary place + sub-places in one universe.
  2. For cross-place teleports, use TeleportAsync (server) + TeleportOptions.
  3. For reserved servers, ReserveServerAsync once, persist the code, reuse it.
  4. Pass non-sensitive hints via teleport data; pass authoritative state via DataStores.
  5. Connect TeleportInitFailed for retry UI.
  6. Validate and rate-limit any client-initiated teleport Remote.
  7. Test in the Roblox app (not Studio) with a published experience.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.