{"id":"hollower233/serverteleport","name":"serverteleport","scope":"hollower233","platform":"roblox","description":"Generic server teleport management: pooled reserved-server registry, heartbeat, discovery, and teleport for persistent shared reserved-server hubs.","version":"0.1.4","latest":"0.1.4","versions":["0.1.0","0.1.1","0.1.2","0.1.3","0.1.4"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"f145f846d503d0ae46f7485cb6cf1b7dcc4a11a66194f2d8dab91ed6a6a02e3c","likes":0,"downloads":0,"install":"forest install hollower233/serverteleport","url":"https://forest.dev/p/roblox/hollower233/serverteleport","files":"https://api.forest.dev/ai/package/roblox/hollower233/serverteleport/files","readme":"# ServerTeleport\n\nGeneric server teleport management for Roblox: pooled reserved-server registry, heartbeat, discovery, and teleport, for games that run one or more persistent shared reserved-server hubs (e.g. a trade hub, a ranked-duel hub) alongside a standard lobby.\n\nThis package is **server-side only**. It has no client API surface beyond `getServerType`, which is safe to call from either realm. Deciding *which* reserved server a player should join (auto-match, browse-and-pick, etc.) is left entirely to the host project — this package only provides the underlying mechanism.\n\n## Install\n\n```toml\n[dependencies]\nServerTeleport = \"hollower233/serverteleport@0.1.0\"\n```\n\n## Concepts\n\n- **Pool**: a named category of reserved server (e.g. `\"tradeServer\"`). A pool's live servers share one MemoryStore-backed registry so any server can discover the others. There is no upfront pool registration — a `poolName` is just a string you pass to `teleport`/`getActiveReservedServers`, and the corresponding registry is created on first use.\n- **Server type**: what a given running server *is* — `\"standard\"` for an ordinary server, or a pool name for a server that was reserved as part of that pool. Resolved once, from `TeleportData`, and published via `getServerType()`.\n\n## API\n\n### `ServerTeleport.server.init(studioSetServer: string?)`\n\nCall once, from your server bootstrap script, before using any other method. Determines this server's type and — if it's a reserved server — starts its heartbeat loop (writes liveness info to the pool's MemoryStore registry every 20s, deregisters on `BindToClose`).\n\nResolution order:\n1. `studioSetServer`, if non-empty — lets you fake a pool name in Studio. Compute this yourself from your own project's flag system (e.g. `GameFlags`) and pass the result in; this package does not read any flag directly.\n2. Otherwise, checks whether this is a real TeleportService-created reserved server (`PrivateServerId` set, `PrivateServerOwnerId == 0`). If not, resolves to `\"standard\"`.\n3. If it is, waits for the first player to join and reads `__poolName` out of their `TeleportData` (written by `server.teleport` at teleport time) to determine which pool this server belongs to.\n\nThis does not yield the calling script — the wait for a player (step 3) runs in the background. Calling `init` more than once is an error.\n\n`game.JobId` is always empty in Studio (only real published servers get one), and `MemoryStoreService` rejects an empty key — so when a Studio session resolves to a reserved server (via `studioSetServer`), the heartbeat loop skips its `MemoryStoreService` writes (one `warn`, not per-heartbeat) instead of failing every 20s. This means `getActiveReservedServers` will never see Studio sessions as candidates — reserved-server discovery is untestable in Studio and only works on real published servers.\n\n### `ServerTeleport.server.teleport(poolName: string, args: TeleportArgs)`\n\nTeleports `args.plrList` to a reserved server in `poolName`. If `args.reservedServerAccessCode` is omitted and `args.targetServer` is `\"reserved\"` (the default), reserves a brand-new server via `TeleportService:ReserveServerAsync`. Embeds `__poolName` (and, when reserving/joining a specific reserved server, `__reservedServerAccessCode`) into the outgoing `TeleportData` so the destination server can identify itself in `init()`.\n\nFailures are `warn`-logged and swallowed (no return value) — the caller does not currently get a programmatic failure signal.\n\n```lua\ntype TeleportArgs = {\n\tplrList: { Player },\n\tteleportData: {}?,\n\ttargetServer: (\"standard\" | \"reserved\")?, -- default \"reserved\"\n\treservedServerAccessCode: string?,\n}\n```\n\n### `ServerTeleport.server.getActiveReservedServers(poolName, sortField?, sortDesc?, cursor?)`\n\nQueries `poolName`'s registry for other currently-active (heartbeat within the last 60s) reserved servers. Use this to build your own join policy (auto-assign to the least-full server, list servers for the player to pick, etc.).\n\n```lua\ntype ReservedServerInfo = {\n\taccessCode: string,\n\tprivateServerId: string,\n\tjobId: string,\n\tplayerCount: number,\n\tupdatedAt: number,\n\tliveTime: number,\n}\n```\n\n- `sortField`: `\"playerCount\"` (default, sorts the \"active by heartbeat time\" table) or `\"liveTime\"` (sorts the \"active by server age\" table).\n- `sortDesc`: defaults to `true`.\n- `cursor`: pass the previously returned cursor to page; a page only returns a cursor when it was full (200 entries), otherwise there's nothing more.\n\n### `ServerTeleport.getServerType(): string`\n\nReturns `\"standard\"` or the current server's pool name. Callable from server or client. If the value isn't resolved yet (e.g. a client script running before the server has finished `init`), waits for it rather than erroring.\n\n## What this package deliberately does not do\n\n- No client-facing join/browse UI or remotes — build your own on top of `getActiveReservedServers`/`teleport`.\n- No admin server-browser tooling.\n- No disconnect/rejoin tracking.\n- No notification UI on teleport start/failure.\n- No networking dependency (no Net, no RemoteEvents) — everything here is either server-only calls or a replicated `Attribute` read.\n","readmeTruncated":false}