mcpField
R
roblox-teleport

roblox-teleport

github.com/nonlooped开发24308 次安装1 周前更新
代码仓库

关于 roblox-teleport

roblox-teleport 是面向 Roblox 开发者的传送系统参考技能,说明如何在体验中的地点和服务器之间移动玩家。内容涵盖服务器端传送、预留服务器、数据传递、匹配模式及安全处理。

主要功能

  1. 01使用 TeleportAsync 进行服务器端传送
  2. 02指定目标服务器或预留服务器
  3. 03跨传送传递数据与加载界面
  4. 04传送失败处理与重试
  5. 05大厅、比赛和服务器浏览器模式
  6. 06请求验证与传送安全

适用场景

  1. 01将大厅玩家送入比赛地点
  2. 02为私人比赛创建预留服务器
  3. 03构建活动服务器列表供玩家选择
  4. 04安全处理玩家发起的传送请求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nonlooped/roblox-suite --skill roblox-teleport

安装后在 Claude Code 中输入/roblox-teleport确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
matchmaking.md
teleport-options.md
scripts/
TeleportHelper.lua
tests/
TeleportHelper.spec.lua

详细介绍

roblox-teleport

官方来源:

TeleportService 可在不同地点和服务器之间传送玩家。现代入口是 TeleportAsync(仅限服务器);旧版的 Teleport(客户端)、TeleportPartyAsync、TeleportToPlaceInstance、TeleportToPrivateServer 和 TeleportToSpawnByName 方法均已弃用。请改用 TeleportAsync()。对于由客户端发起的传送,请向服务器触发 RemoteEvent,并由服务器调用 TeleportAsync();即使地点配置为“Fully Open”,新功能也不要从客户端调用 Teleport()。请参阅迁移到安全传送指南。

交叉参考:

多地点架构

一个宇宙(体验)可以包含多个地点。每个地点都有自己的 PlaceId;宇宙有一个 UniverseId(以 game.GameId 的形式公开)。主地点是玩家从 Roblox 应用的主入口加入的地点。

  • 在 Creator Dashboard 中的体验 → Places 下创建其他地点。
  • 每个地点都必须已发布,并且(要允许玩家加入)可见或已启用。
  • game.PlaceId 是当前地点;game.GameId 是宇宙 ID(有时称为 UniverseId)。
  • 同一宇宙中的地点共享 DataStore、MemoryStore 和 Asset 权限。
  • 默认情况下,跨宇宙(跨体验)传送受到限制。请参阅下文的“跨体验传送”。

TeleportAsync(现代方法)

TeleportService:TeleportAsync(placeId, players, teleportOptions?) 是统一的单一方法。它可以将玩家传送到不同地点、特定服务器或预留服务器。仅限服务器。从客户端调用会报错。Teleport() 和其他旧版方法现在会显示弃用提示,引导你使用 TeleportAsync()(请参阅地点之间传送)。

--!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
群组传送限制
  • 群组只能在同一体验内传送(同一宇宙)。
  • 每次 TeleportAsync 调用最多可传送 50 名玩家。
冲突的 TeleportOptions

以下组合会导致“参数不兼容”错误:

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

TeleportOptions

属性用途
ServerInstanceId通过 JobId 指定目标服务器。
ReservedServerAccessCode使用访问代码加入现有的预留服务器。
ShouldReserveServer创建新的预留服务器并传送到其中。
SetTeleportData(data) / GetTeleportData()将数据传递到目标地点(通过 GetLocalPlayerTeleportData 获取)。

TeleportAsyncResult

传入 TeleportOptions 时,TeleportAsync 会返回一个 TeleportAsyncResult,其中包含最终传送目的地的信息。可用于确认玩家最终到达的位置。

TeleportInitFailed(错误处理)

当传送无法启动时,会触发 TeleportService.TeleportInitFailed。连接到此事件以显示重试界面。失败原因包括无效的 place ID、玩家列表为空、实例不是 Player、传送选项类型错误、由客户端调用 TeleportAsync,以及参数冲突。

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

保留服务器

TeleportService:ReserveServerAsync(placeId)(仅限服务器端,会让出执行权)会返回访问码和一个 PrivateServerId。玩家只能通过该访问码加入保留服务器,无法通过常规匹配加入。

  • 首次使用访问码时,服务器会启动。
  • 访问码会一直有效;即使当前没有服务器运行,也可以重新加入保留服务器(系统会启动一个新服务器)。
  • 检测是否处于保留服务器环境:local isReserved = game.PrivateServerId ~= "" and game.PrivateServerOwnerId == 0。
  • 同一访问码对应的不同服务器实例,其 PrivateServerId 相同;JobId 则不同。
  • **跨平台注意事项:**关闭跨平台联机的 Xbox/PlayStation 玩家会进入与启用跨平台联机的玩家不同的服务器。多个服务器可以共用一个 PrivateServerId。使用 game.MatchmakingType 进行区分。
--!strict
local TeleportService = game:GetService("TeleportService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

-- Reserve a candidate, then atomically publish-or-use the winner.
local store = DataStoreService:GetDataStore("ReservedServerAllocations_v2")
local reserveOk, candidateCode = pcall(function()
    return select(1, TeleportService:ReserveServerAsync(game.PlaceId))
end)
local code: string? = nil
if reserveOk and typeof(candidateCode) == "string" then
    local now = os.time()
    local candidate = { accessCode = candidateCode, expiresAt = now + 3600, state = "allocated" }
    local publishOk, winner = pcall(function()
        return store:UpdateAsync("DungeonRoom_1", function(current)
            if type(current) == "table" and type(current.accessCode) == "string" and current.expiresAt > now then
                return current
            end
            return candidate
        end)
    end)
    if publishOk and type(winner) == "table" then
        code = winner.accessCode -- every racing allocator uses the same winner
    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

跨传送传递数据

有两种机制,安全性各不相同:

TeleportOptions:SetTeleportData / GetLocalPlayerTeleportData

在源服务器上(通过 TeleportOptions)设置,然后在目标客户端上通过 TeleportService:GetLocalPlayerTeleportData() 获取。**只能在客户端获取。**可以传递任何可序列化的值。

**安全性:**利用漏洞的用户可以伪造传送数据。敏感数据(货币、权益)应通过 DataStoreService 发送;传送数据仅用于非敏感提示(所选游戏模式、外观装备偏好)。

SetTeleportSetting / GetTeleportSetting

仅限客户端,在本地存储,并在同一游戏内的传送过程中保留。适用于无需服务器信任的客户端偏好设置(蹲伏状态、UI 标签页)。此数据也可被伪造。任何影响游戏玩法的内容都应在服务器端验证。

自定义加载界面

TeleportService:SetTeleportGui(gui)(客户端)会设置一个在传送过程中显示的 ScreenGui。TeleportService:GetArrivingTeleportGui()(客户端,在目标位置调用)会获取该界面,以便你将其设为 PlayerGui 的子对象并运行自定义过渡效果。传送 GUI 不会跨越到不同的游戏(universe),也不会在多次传送之间保留。每次传送前都要设置。

--!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

跨体验传送

默认情况下,从你的体验传送到其他人拥有的体验会失败。如需启用跨体验传送,请按照官方地点之间的传送文档中的步骤操作。在你自己的 universe 内部(跨地点)传送无需额外设置即可使用。

TeleportService:PromptExperienceDetailsAsync(player, universeId)(仅限客户端,会让出执行权)会向玩家显示体验详情和加入按钮。如果玩家不符合加入目标体验的资格,该按钮会被禁用。在 Studio 中进行游戏测试时,加入按钮始终处于禁用状态。

匹配模式

模式 1:大厅 → 对局(简单轮询)

大厅地点收集玩家;准备就绪的玩家达到一定数量后,TeleportAsync 会将他们发送到游戏地点。如果有多个大厅,请使用 MessagingService 在不同的大厅服务器之间协调。

模式 2:每场对局使用一个保留服务器

适用于副本地下城或私人对局:ReserveServerAsync → 将访问码按对局 ID 存入 DataStore → 使用 ReservedServerAccessCode 传送队伍 → 玩家进入隔离服务器。

模式 3:通过 MemoryStoreService 实现服务器浏览器

维护一个 MemoryStoreService 排序映射,其中记录活跃服务器(以 JobId 为键,玩家人数/模式/延迟为值)。大厅列出这些条目;玩家选中某个服务器后,设置 TeleportOptions.ServerInstanceId = jobId 并调用 TeleportAsync。每个游戏服务器在玩家加入/离开时更新排序映射。与服务器数量较多时使用 MessagingService 相比,这种方式的扩展性更好。

模式 4:外部匹配服务

对于复杂的匹配(基于技能、队伍队列),使用外部 HTTP 服务作为匹配器。它会返回 placeId + JobId,或一个新的预留服务器访问代码;随后由大厅调用 TeleportAsync 传送整个队伍。有关 HttpService 安全模型和游戏内 Open Cloud 调用,请参阅 roblox-networking 和 roblox-open-cloud。

所有匹配都必须遵守每次 TeleportAsync 最多传送 50 名玩家的限制;更大的队伍需拆分成多次调用。

通过传送交接 DataStore 数据

要在不同地点之间转移玩家状态:

  1. **源服务器:**触发事件时,通过 UpdateAsync 保存玩家档案(参阅 roblox-datastores)。然后进行传送。
  2. **目标服务器:**在 PlayerAdded 时,通过 GetAsync 加载档案(如果刚刚保存过,可考虑使用 UseCache=false 以获取最新数据)。
  3. **竞态条件:**如果目标服务器加载时保存尚未写入完成,你会得到过时数据。可通过以下方式缓解:(a)如果数据很重要,在传送前等待保存确认;或(b)在传送数据中传入“加载此版本”的提示,并重试加载,直到版本匹配。
  4. **绝不要将传送数据视为权威状态。**传送数据只是提示;DataStore 才是真实数据源。

安全性

  • **TeleportAsync 只能由服务器调用。**客户端不能直接调用它。如果需要由客户端发起传送,客户端应发送 RemoteEvent,由服务器验证后再调用 TeleportAsync。旧版客户端方法 Teleport() 现已弃用(请在服务器上使用 TeleportAsync());请参阅上文的迁移指南。
  • **对每位玩家的传送请求进行速率限制。**恶意客户端反复发送“传送我”的 RemoteEvent,可能扰乱服务器流程;如果每次触发都会保存数据,还会消耗 DataStore 预算。
  • **验证目标地点。**不要未经清理就让客户端提供的地点 ID 决定传送目标。在服务器端将允许的目标地点列入白名单。
  • **传送数据可被伪造。**将其视为提示;在目标服务器上通过 DataStore 重新验证任何会影响游戏的声明。
  • GetLocalPlayerTeleportData 只能由客户端调用,且返回由客户端控制的数据。不要在服务器上将其用于权威决策。

Studio 限制

**TeleportService 在 Studio 游戏测试期间无法使用。**你必须发布体验,并在 Roblox 应用程序中测试传送。请在测试工作流中考虑这一点。传送逻辑只能在线上验证。

注意事项

  • 玩家传送后会失去角色状态(新地点会生成新角色)。请通过 DataStore 保存/加载数据;不要尝试保留实时角色状态。
  • 源服务器上的 BindToClose 可能与传送发生竞态。如果数据很重要,请在触发传送前完成保存。
  • 默认传送加载消息已移除;CustomizedTeleportUI 已弃用且不起作用。
  • 跨平台情况下(关闭跨平台游戏的 Xbox/PS),一个预留服务器可能会被拆分为多个具有相同 PrivateServerId 的服务器。
  • 队伍传送仅限同一 universe;你无法通过 TeleportAsync 将队伍传送到其他体验。
  • 50 名玩家的限制按每次调用计算。更大的队伍需要多次调用;若要确保“同时抵达”,调用顺序很重要。
  • 跟随好友:Players.PlayerAdded → 检查 player.FollowUserId → GetPlayerPlaceInstanceAsync(followId) → TeleportToPlaceInstance。注意,GetPlayerPlaceInstanceAsync 返回的是 JobId,而不是预留服务器访问代码,因此无法用于跟随好友进入预留服务器。

脚本

  • scripts/TeleportHelper.lua:经过审查的服务端示例,展示了原子化发布预留代码、过期处理、分配结果不明确时的协调,以及分开的 teleporting/arrived 状态。

后续步骤

  1. 确定你的场所架构:在同一个 universe 中设置主场所和子场所。
  2. 跨场所传送时,使用 TeleportAsync(服务端)和 TeleportOptions。
  3. 对于预留服务端,先预留一个候选项,再通过 UpdateAsync 发布;使用最终胜出的分配结果。分别跟踪传送的发起和抵达目的地。
  4. 通过传送数据传递非敏感提示;通过 DataStores 传递权威状态。
  5. 连接 TeleportInitFailed,以提供重试 UI。
  6. 验证并限制任何由客户端发起的传送 Remote 的调用频率。
  7. 使用已发布的体验,在 Roblox 应用中(而非 Studio)进行测试。

参考索引

评论

支持 Markdown