mcpField
R
roblox-teleport

roblox-teleport

github.com/nonlooped開発24308 回インストール1 週間前に更新
リポジトリ

roblox-teleport について

roblox-teleportは、Roblox開発者向けに、体験内のプレイスやサーバー間でプレイヤーを移動する方法を解説するスキルです。サーバー側のテレポート、予約サーバー、データ受け渡し、マッチメイキング、安全対策を扱います。

主な機能

  1. 01TeleportAsyncによるサーバー側テレポート
  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 は、プレイヤーを Place 間およびサーバー間で移動させます。現在の標準的な入口は TeleportAsync(サーバー専用)です。旧式の Teleport(クライアント)、TeleportPartyAsync、TeleportToPlaceInstance、TeleportToPrivateServer、TeleportToSpawnByName メソッドは非推奨です。代わりに TeleportAsync() を使用してください。クライアントからテレポートを開始する場合は、RemoteEvent をサーバーに送信し、サーバーから TeleportAsync() を呼び出してください。新しい実装では、Place が「Fully Open」に設定されていても、クライアントから Teleport() を呼び出さないでください。安全なテレポートへの移行ガイドを参照してください。

相互参照:

複数の Place によるアーキテクチャ

ユニバース(エクスペリエンス)には、複数のPlaceを含めることができます。各 Place には独自の PlaceId があり、ユニバースには UniverseId(game.GameId として公開されます)があります。プライマリ Placeは、プレイヤーが Roblox アプリのメインエントリーポイントから参加する Place です。

  • Creator Dashboard のエクスペリエンス → Places から Place を追加します。
  • 各 Place は公開する必要があり、参加可能にするには表示または有効にする必要があります。
  • game.PlaceId は現在の Place、game.GameId はユニバース ID(UniverseId と呼ばれることもあります)です。
  • 同じユニバース内の Place は、DataStore、MemoryStore、アセットの権限を共有します。
  • ユニバース間(エクスペリエンス間)のテレポートは、デフォルトで制限されています。詳しくは、以下の「エクスペリエンス間のテレポート」を参照してください。

TeleportAsync(現在の標準メソッド)

TeleportService:TeleportAsync(placeId, players, teleportOptions?) は、統一された単一のメソッドです。別の Place、特定のサーバー、または予約済みサーバーへのテレポートに使用できます。サーバー専用です。クライアントから呼び出すとエラーになります。Teleport() およびその他の旧式メソッドには、TeleportAsync() を使用するよう案内する非推奨メッセージが表示されます(Place 間のテレポートを参照)。

--!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 1 回あたりのプレイヤー数は最大 50 人です。
TeleportOptions の競合

次の組み合わせでは、「Incompatible Parameters」エラーが発生します。

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

TeleportOptions

プロパティ用途
ServerInstanceIdJobId で特定のサーバーを指定します。
ReservedServerAccessCodeアクセスコードを使って、既存の予約済みサーバーに参加します。
ShouldReserveServer新しい予約済みサーバーを作成し、そこにテレポートします。
SetTeleportData(data) / GetTeleportData()移動先にデータを渡します(GetLocalPlayerTeleportData で取得します)。

TeleportAsyncResult

TeleportOptions を渡すと、TeleportAsync は最終的なテレポート先の情報を含む TeleportAsyncResult を返します。プレイヤーがどこに移動したかを確認する際に役立ちます。

TeleportInitFailed(エラー処理)

TeleportService.TeleportInitFailed は、テレポートの開始に失敗したときに発生します。再試行 UI を表示するには、このイベントに接続します。失敗の理由には、無効な 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)(サーバー専用、yield あり)は、アクセスコードと 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

テレポート間でデータを渡す

セキュリティ特性が異なる、2 つの方法があります。

TeleportOptions:SetTeleportData / GetLocalPlayerTeleportData

送信元サーバーで(TeleportOptions を介して)設定し、移動先のクライアントで TeleportService:GetLocalPlayerTeleportData() を介して取得します。取得はクライアントのみで行えます。 シリアライズ可能な値であれば、どのような値でも渡せます。

セキュリティ: 悪意のあるユーザーはテレポートデータを偽装できます。通貨や権利などの機密データは、代わりに DataStoreService を通じて送信し、テレポートデータは選択したゲームモードやコスメティックのロードアウト設定など、機密性のないヒントにのみ使用してください。

SetTeleportSetting / GetTeleportSetting

クライアント専用で、ローカルに保存され、同じゲーム内でのテレポート間も保持されます。しゃがみ状態や UI タブなど、サーバーの信頼を必要としないクライアント側の設定に適しています。これも偽装可能です。ゲームプレイに影響するものは、必ずサーバー側で検証してください。

カスタム読み込み画面

TeleportService:SetTeleportGui(gui)(クライアント)は、テレポート中に表示する ScreenGui を設定します。TeleportService:GetArrivingTeleportGui()(移動先のクライアント)はその GUI を取得します。取得した GUI を PlayerGui の子に設定し、独自の画面遷移を実行できます。テレポート GUI は別のゲーム(ユニバース)には引き継がれず、複数回のテレポートをまたいで保持されることもありません。テレポートのたびに設定してください。

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

エクスペリエンス間のテレポート

自分のエクスペリエンスから他者が所有する別のエクスペリエンスへのテレポートは、デフォルトでは失敗します。エクスペリエンス間のテレポートを有効にするには、公式ドキュメント「プレース間のテレポート」の手順に従ってください。同じユニバース内(別のプレースへのテレポート)であれば、追加設定なしでテレポートできます。

TeleportService:PromptExperienceDetailsAsync(player, universeId)(クライアント専用、yield あり)は、エクスペリエンスの詳細と参加ボタンをプレイヤーに表示します。プレイヤーが移動先のエクスペリエンスに参加できない場合、ボタンは無効になります。Studio でプレイテスト中は、参加ボタンが常に無効になります。

マッチメイキングのパターン

パターン 1:ロビー → マッチ(シンプルなラウンドロビン)

ロビーのプレースでプレイヤーを集め、必要な人数が揃ったら TeleportAsync でゲームのプレースに送ります。ロビーが複数ある場合は、MessagingService を使ってロビーサーバー間を調整します。

パターン 2:マッチごとの予約サーバー

インスタンス化されたダンジョンやプライベートマッチの場合:ReserveServerAsync → マッチ ID をキーにしてアクセスコードを DataStore に保存 → ReservedServerAccessCode を使ってパーティーをテレポート → プレイヤーが隔離されたサーバーに到着します。

パターン 3:MemoryStoreService を使ったサーバーブラウザー

アクティブなサーバーを管理する MemoryStoreService のソート済みマップを維持します(キーは JobId、値はプレイヤー数/モード/ping です)。ロビーにエントリーを一覧表示し、プレイヤーがサーバーを選んだら、TeleportOptions.ServerInstanceId = jobId を設定して TeleportAsync を呼び出します。各ゲームサーバーから、プレイヤーの参加/退出時にソート済みマップを更新します。多くのサーバーがある場合、これは MessagingService よりもスケーラビリティに優れています。

パターン 4:外部のマッチメイキングサービス

スキルベースやパーティーキューなど、複雑なマッチメイキングには、外部 HTTP サービスをマッチメーカーとして使用します。サービスは placeId と JobId、または新しい予約サーバーのアクセスコードを返し、ロビーはパーティーに対して TeleportAsync を呼び出します。HttpService のセキュリティモデルについては roblox-networking を、ゲーム内からの Open Cloud 呼び出しについては roblox-open-cloud を参照してください。

すべてのマッチメイキングでは、**TeleportAsync 1 回あたりのプレイヤー数上限(50 人)**を守る必要があります。これを超えるグループは、複数回の呼び出しに分割してください。

テレポート間での DataStore による引き継ぎ

プレイヤーの状態を別の場所に移動するには、次の手順に従います。

  1. 移動元サーバー: イベント発生時に、UpdateAsync を使ってプレイヤーのプロフィールを保存します(roblox-datastores を参照)。その後、テレポートします。
  2. 移動先サーバー: PlayerAdded で、GetAsync を使ってプロフィールを読み込みます(直前に保存したデータを新たに読み込む場合は、UseCache=false を検討してください)。
  3. 競合状態: 移動先でデータを読み込む時点までに保存処理が完了していない場合、古いデータを取得することがあります。次の方法で対処してください。(a) データが重要な場合は、テレポートの前に保存の完了を待つ、または (b) テレポートデータに「このバージョンを読み込む」というヒントを渡し、読み込んだデータが一致するまで再試行する。
  4. テレポートデータを正しい状態の根拠として決して信頼しないでください。 テレポートデータはヒントであり、正しい情報は DataStore にあります。

セキュリティ

  • TeleportAsync はサーバー専用です。 クライアントから直接呼び出すことはできません。クライアント起点のテレポートが必要な場合は、クライアントから RemoteEvent を送信し、サーバーが検証してから TeleportAsync を呼び出します。従来のクライアントメソッド Teleport() は非推奨になりました(サーバーで TeleportAsync() を使用してください)。上記の移行ガイドを参照してください。
  • プレイヤーごとにテレポートリクエストをレート制限してください。「テレポートして」という Remotes を悪意のあるクライアントが連続送信すると、サーバーの処理を妨げたり、トリガーのたびに保存している場合は DataStore の予算を消費したりする可能性があります。
  • 移動先を検証してください。 クライアントから渡された place ID を未検証のままテレポートに使用しないでください。サーバー側で許可する移動先をホワイトリスト化してください。
  • テレポートデータは偽装可能です。 ヒントとして扱い、ゲームプレイに影響する主張は、移動先サーバーで DataStore に照らして再検証してください。
  • GetLocalPlayerTeleportData はクライアント専用で、クライアントが制御するデータを返します。サーバー側で正しい状態を判断するために使用しないでください。

Studio の制限事項

TeleportService は Studio でのプレイテスト中には動作しません。 エクスペリエンスを公開し、Roblox アプリケーションでテレポートをテストする必要があります。テストのワークフローでは、この点を考慮してください。テレポートの動作を検証できるのは、公開環境のみです。

注意点

  • テレポートをまたぐと、プレイヤーのキャラクターの状態は失われます(新しい場所では新しいキャラクターが作成されます)。DataStore を使って保存/読み込みを行い、キャラクターの状態をそのまま引き継ごうとしないでください。
  • 移動元サーバーの BindToClose とテレポートが競合する可能性があります。データが重要な場合は、テレポートを開始する前に保存を完了してください。
  • デフォルトのテレポート読み込みメッセージは削除されました。CustomizedTeleportUI は非推奨で、何も行いません。
  • クロスプラットフォーム(クロスプレイがオフの Xbox/PS)では、同じ PrivateServerId を持つ複数のサーバーに予約サーバーが分割されることがあります。
  • グループテレポートは同一ユニバース内に限られます。グループを別のエクスペリエンスへ TeleportAsync で移動することはできません。
  • 50 人の上限は呼び出しごとに適用されます。これを超えるグループは複数回の呼び出しが必要です。また、「一緒に到着する」動作を実現するには、呼び出しの順序が重要です。
  • フレンドを追って参加する場合:Players.PlayerAdded → player.FollowUserId を確認 → GetPlayerPlaceInstanceAsync(followId) → TeleportToPlaceInstance。GetPlayerPlaceInstanceAsync が返すのは JobId であり、予約サーバーのアクセスコードではないため、予約サーバーにいるフレンドへの参加には使えない点に注意してください。

スクリプト

  • scripts/TeleportHelper.lua:予約コードのアトミックな公開、有効期限、割り当ての不確定な状態の調整、teleporting/arrived 状態の分離を含む、レビュー済みのサーバー側の例です。

進め方

  1. プレイスの構成を決めます:1 つのユニバース内にメインプレイスとサブプレイスを配置します。
  2. プレイス間のテレポートには、TeleportAsync(サーバー)と TeleportOptions を使用します。
  3. 予約サーバーでは候補を予約し、UpdateAsync を通じて公開します。勝った割り当てを使用してください。テレポートの開始と目的地への到着は別々に追跡します。
  4. 機密情報でないヒントはテレポートデータで渡し、信頼できる状態情報は DataStores で渡します。
  5. 再試行用 UI のために TeleportInitFailed に接続します。
  6. クライアントから開始されるテレポート Remote を検証し、レート制限を設定します。
  7. 公開済みのエクスペリエンスを使い、Roblox アプリ(Studio ではありません)でテストします。

参照インデックス

  • matchmaking.md:ロビー、予約サーバー、割り当て競合、またはプレイヤーデータの引き継ぎを調整します。
  • teleport-options.md:目的地のサーバーを選択するか、テレポート結果を確認します。

コメント