Contents
Builda Online
Builda Online provides hosted multiplayer for Builda games. Your game keeps its own client presentation and gameplay rules, while the platform handles player identity, matchmaking, room scheduling, and multiplayer transport. Room rules are defined by your TypeScript or Rust Room source code.
Copy for AI
Please integrate Builda Online into the current project: read https://ai.builda.game/online/setup.md and strictly follow the installation and workflow instructions in it.
Choose the Player Experience
First decide how players enter the game, start a match, and what happens when it ends. A game can offer several entry points at once, but each entry point should belong to one clearly defined mode.
| What players experience | Good for | Multiplayer mode |
|---|---|---|
| Gather a fixed number of players for one match, then return to the lobby to find new players | 1v1, multiplayer battles, co-op levels | Single-match matchmaking |
| Players stay in the same room and play match after match; others can join while waiting | CF/CS-style rooms, board game rooms | Recurring room |
| Players enter a world that is already running; newcomers can join at any time | MMORPG channels, shadow worlds, long-running servers | Persistent room |
| Players open a room directly and invite friends through the Builda App or share the Room ID | Matches with friends, private co-op | Private room |
Each gameplay mode is identified by a matchKey, which maps to a Room program, player count, teams, and runtime
configuration. The same gameplay mode can offer two independent entry points:
- Public entry:
publicMatchmaking=true; the publicmode(Room, Pool, or RankPool) determines how matchmaking works. - Private entry:
privateRoomsEnabled=true; rooms are created directly and other players join by Room ID, without public matchmaking or Confirm.
The two entry points can be enabled individually, both enabled, or both disabled. Private rooms have no host: the creator and members have equal rights, and the room is still subject to team capacity and room lifecycle limits. Public Pool/RankPool matches already have a fixed participant list, so knowing the Room ID does not let anyone join. The AI will go on to confirm, for each entry point, the player count, when joining is allowed, how disconnects are recovered, and where players go after a match ends.
Ranked scores: when the public mode is RankPool, the platform pairs players by scores the game submits. Each
entry passes rankScore (rank_score in Godot, MatchJoinOptions.RankScore in Unity). Scores must be integers
from 0 to 9007199254740991. The platform only validates the range, not whether a score is genuine; if wins, rewards
or rankings depend on it, the game must design its own anti-tampering approach.
Supported Scope
Each project installs only one client SDK, one Room language, and the local Mock for that language.
| Component | Currently supported |
|---|---|
| Game client | Godot, H5, Cocos Creator 3.8 Web, Unity |
| Room language | TypeScript, Rust |
| Gameplay data plane | WebRTC DataChannel |
| Room entry | Public automatic matchmaking, private room creation and joining by Room ID |
| Player data and purchase delivery | Read and write cross-room player data in the Room; in-app purchase orders with grantBy "room" are delivered by the Room |
| Builda App multiplayer social features | Room chat group, room invitations that launch the game (provided by the Builda App; the game does not need to build them) |
| Local integration testing | TypeScript or Rust source Mock |
The client handles input, UI, and presentation; the Room validates player actions, advances authoritative state, broadcasts results, handles disconnects, and settles results. Do not leave outcomes, damage, resources, or rankings that must be tamper-proof to the client alone.
Player Data
Player Data is stored per game + player, persists across rooms, and can only be read and written in the Room. The
client has no API for it (privateKV is a separate private save store). TypeScript Rooms use cloudEnqueue /
cloudRead* / cloudFlush / cloudLoadSnapshot; Rust Rooms use the corresponding cloud_state_api.
- A successful
cloudEnqueueonly means the write is queued; when you need to confirm a batch of writes has taken effect, callcloudFlushand wait for applied. - Snapshots you already hold are not refreshed automatically; reload the snapshot after applied, or have the player re-enter the room, to read the new values.
- Keys starting with
builda.are reserved by the platform; the game must not read or write them.
Report semantic issues or failures to the Player Data maintainers; do not treat them as client SDK problems.
Purchase delivery
Each in-app purchase point in payPoints of builda.publish.json declares who delivers it with grantBy:
"client"(omitted or empty string means the same): the client delivers idempotently and then callsBuilda.iap.claim(orderId); the Room is not involved."room": must be declared explicitly. After a successful payment the client sends theorderIdto the Room in a Match message and the Room delivers; the client neither delivers nor claims.
Room delivery flow:
- On receiving an
orderId, calldoOrder/do_order; the result arrives in the callback on a later tick. Do not block waiting for it. - Only when the result is
allowed, usefulfillin the same callback to write that player's data (for example, add coins). Take the item from thepayIdin the callback result, never from an item ID uploaded by the client; the recipient is always the authenticated player who sent the message. - Do not call
cloudEnqueue/cloudFlushinside the payment callback; the platform waits for thefulfillwrites to take effect before completing the order. already_deliveredmeans it was already delivered; do not treat it as a rejection. An incomplete order can be retried withdoOrderon the sameorderId.
Chat and Invitations Built into the Builda App
You do not need to build your own server or UI for in-room chat and friend invitations. The Builda App already provides them, and the SDK connects your game to them at the right moments.
Room Chat
- The Builda App provides one chat group per Room; the group is the Room itself. Once members enter the room, they can chat in the App's chat interface.
- After the game obtains a Room, it has the SDK join the group and tells the App which team the player belongs to
(
team_idis defined by the game). When the player leaves the room, the Room closes, reconnection times out, or the connection is closed, the SDK leaves the group automatically; the game does not need to handle this. - Sending, displaying, and moderating chat messages all happen inside the Builda App. Group messages pushed by the App are also delivered to the game, which can use them for speech bubbles or combat logs, but the game cannot send messages through Online.
- When chat is not available in the current environment (local Mock, opened directly in a browser, older Builda App versions), the SDK reports it as unavailable. The game should hide the chat entry point and should not retry or build its own replacement.
Room Invitations and Joining
- After a player enters any Room, the SDK automatically tells the Builda App which room they are in, and the App uses this to show the multiplayer members entry point and offer friend invitations. Sending invitations is handled by the Builda App; Online does not provide an API for sending invitations.
- When an invited player taps the invitation in the Builda App, the App launches the game and passes the invitation to it. The game reads the invitation once during startup and, after getting the Room ID, joins by that ID; without an invitation, it follows the normal lobby flow.
- An invitation is only a hint; the platform still decides whether the player can enter. When the room is full or closed, or a public Pool/RankPool match already has a fixed participant list, show the player a message based on the stable error code.
- The game can also share Room IDs through its own social system. Other players join by Room ID, with the same result as an App invitation.
Chat and invitations work exactly the same for public and private rooms. During integration, the AI will decide, based
on your gameplay, where to place the chat entry point, how to divide team_id, and how to handle invitations at startup.
From Integration to Launch
- Confirm the project. The project should already be integrated with Builda and have a single identifiable client engine.
- Hand it to the AI. Click "Copy for AI" in the top-right corner of the page and send the full integration instructions to the AI in your project. The AI will install the Skill, SDK, and Mock that match your project.
- Confirm the plan. Confirm, in player terms, the matchmaking entry points, room size, room lifecycle, Bots, and disconnect and settlement rules.
- Test locally. Use two different local identities to verify joining, state, messages, disconnects, and leaving.
- Deploy the Room. The AI uploads the Room source and activates the online version only when you explicitly ask it to deploy or go live.
- Verify for real. Use two real identities to verify matchmaking, entering the Room, and gameplay messages before deciding whether to release the client publicly.
A Room deployment may immediately affect newly created real rooms under the same compatible revision. Client-only updates, compatible Room updates, and incompatible protocol updates each have a different release order. Let the installed Skill decide, and do not use repeated uploads as trial and error.
Local vs. Online
| Local | Online | |
|---|---|---|
| Identity | Synthetic local identity | Real Builda App identity |
| Room | Runs the source in the current language directly | Rebuilt and hosted by the platform |
| Transport | Local loopback WebSocket | WebRTC DataChannel |
| Chat and invitations | Not available; the SDK returns an unavailable status | Provided by the Builda App |
| Player data and purchase delivery | The Mock simulates player data and fake-order delivery | Real player data and Builda payments |
| Can verify | Room rules, messages, state, and basic lifecycle | Real authentication, matchmaking, WebRTC, chat and invitations, hosted runtime |
| Does not represent | Production capacity, security isolation, TURN, distributed recovery | A fast local iteration environment |
The TypeScript / Rust local Mocks can simulate player data and doOrder / fulfill for fake orders, so you can run the
full delivery loop without going online; they do not stand in for production storage or real payments, so full
acceptance must still happen online.
The page address, localhost, or 127.0.0.1 cannot be used to tell whether the game is connected to the local Mock.
The Builda App may also load a real game Bundle from a local address on the device; the client only uses the install
record and the explicit configuration provided by the Builda local development shell.
Usage Limits
The following are the current hard limits per Room and per deployment, not targets to fill up. Requests that exceed structural or runtime limits may be rejected, and Rooms that far exceed them may be shut down for protection. Exceeding log limits only drops logs and does not block gameplay logic.
| Room runtime resource | Current limit |
|---|---|
| Players per Room | 50 players |
| Memory per Room | 64 MiB |
| Single complete gameplay message frame | 65535 bytes |
| Single recovery checkpoint | 4 MiB |
| Commands submitted to the platform per lifecycle callback | 256 |
| Output per callback / per Room per second | 1 MiB / 4 MiB |
Game Data Bandwidth
All online Rooms enforce bandwidth limits at both the user level and the room level at the same time. Upstream means
client to Room, downstream means Room to client, and 1 KB = 1024 bytes. The quota covers complete gameplay message
frames plus protocol overhead, not just the business payload.
| Scope | Upstream base rate | Downstream base rate |
|---|---|---|
| Per user | 10 KB/s | 30 KB/s |
| Per Room | 10 × effective player count KB/s | 30 × effective player count KB/s |
The effective player count is min(currently connected unique users + 1, room max players). For example, in a room with
a maximum of 5 players, when 1 player is online the room gets a 2-player quota, and when full it gets a 5-player quota;
each user is still subject to the per-user quota. Multiple connections from the same user share the per-user quota,
and switching online transport or reconnecting does not reset the quota immediately. Broadcasts count toward the
quota per receiving connection; targeted sends count only the target connections.
Each scope and each direction must satisfy both conditions: the total over the past 5 seconds must not exceed base rate × 5 seconds, and the total over the past 1 second must not exceed base rate × 2 seconds. For example, a single user's upstream can send a burst of 20 KB when quota is available, but no more than 50 KB in total over any 5 seconds, so it cannot sustain 20 KB per second. Accounting uses 100ms buckets, and window boundaries may be conservatively delayed by up to 100ms. Recovery depends on when past usage expires; waiting one second does not guarantee recovery.
When a limit is exceeded, the affected gameplay messages are rejected and ROOM_BANDWIDTH_LIMIT_EXCEEDED is reported.
Exceeding bandwidth does not by itself close the Room; new messages can be processed once the quota recovers, but
rejected messages are not resent automatically. The reliable channel also cannot guarantee that over-quota messages
are accepted. Critical actions need business-level acknowledgment and bounded, idempotent retries, not immediate
repeated resends. Limit-exceeded notifications, join/leave acknowledgments, and migration notifications do not count
toward the gameplay quota.
In the table above, 1 MiB / 4 MiB is the runtime budget for output that Room callbacks submit to the platform, not network bandwidth available to players. At runtime, one broadcast counts as a single output, but when actually sent, its bytes are counted per receiving connection and must also satisfy the network limits. A single frame under 65535 bytes does not mean the current bandwidth will allow it to be sent, and splitting into chunks cannot bypass the cumulative limits.
When designing synchronization, estimate worst-case output as "encoded message size × sends per second × actual receiving connections". Prefer compact binary encoding, delta sync, merging and rate limiting, and sending targeted by team or visibility range; include full snapshots and reconnection resends in the budget too.
Source, Configuration, and Logs
| Source and configuration | Current limit |
|---|---|
matchKeys |
64 entries |
| TypeScript single file / source archive | 16 MiB |
| Workspace file count / total unpacked size | 2048 files / 64 MiB |
| Match confirmation timeout | 1..60 seconds |
| Snapshot schema version | 1..65535 |
| Room logs | Current limit |
|---|---|
| Single entry body | 2048 bytes |
| Per Room per second | 20 entries, 16 KiB of body |
| Memory buffer / single query | 256 KiB / 200 entries |
| Maximum retention | 7 days |
Split large state into summaries, deltas, or pages. Do not keep unbounded collections, call platform capabilities once per object, or broadcast the full world state every tick. Business state, retries, and settlement must not depend on logs being written successfully. The local Mock enforces only some message and log limits; it does not simulate online WASM memory, checkpoints, output budgets, online dual-window bandwidth limits, or real capacity, so passing locally does not prove that online capacity requirements are met.
Current Delivery Information
This document was updated on 2026-10-23. The page, Skills, SDKs, and Mocks are accepted and switched together as part of the same CDN snapshot.
| Component | Version |
|---|---|
| Multiplayer development Skill | 0.36.0 |
| Log diagnostics Skill | 0.5.0 |
| Godot / H5 / Cocos / Unity client SDK | 0.20.0 / 0.18.0 / 0.11.0 / 0.19.0 |
| TypeScript / Rust Room SDK | 0.14.11 / 0.14.11 |
| TypeScript / Rust local Mock | 0.6.18 / 0.5.19 |
| Room public contract | 0.12.0 |
Multiplayer deliverables are released with the same version as the Builda SDK. Each project pins a single version in
.builda-agent/sdk-version and is not upgraded automatically just because you start a new multiplayer feature. Only
after you have the AI run builda sdk upgrade to advance the version does builda-online upgrade update the
multiplayer Skill, client SDK, Room SDK, and local Mock together to the same version, rather than replacing individual
files.
FAQ
Do I need to learn the entire API myself?
No. First confirm the player experience and rules, then hand the installation entry point to your project's AI. The AI should read only the public materials relevant to your current engine and Room language, and complete the full loop of client, Room, configuration, and local verification.
Can I go live once local tests pass?
No. The local Mock is good for verifying business rules, but it does not simulate real authentication, the matchmaking algorithm, WebRTC/TURN, capacity, or security isolation. Before going live, you still need to create a new Ticket and Room with real identities to verify the current online Artifact.
Can I use both TypeScript and Rust Rooms?
Each project should choose one Room language. If source code already exists, follow what the project already uses; for a new project where this cannot be determined, the author chooses. The client engine and Room language are independent of each other; for example, a Unity client can be paired with a TypeScript Room.
Do I need to build chat and friend invitations myself?
No. The room chat group and room invitations are provided by the Builda App, and the SDK connects to them automatically on entering the room, leaving the room, and at startup. The game only needs to decide where to place the chat entry point and how to divide teams, and handle the invitation once at startup. The local Mock does not simulate either feature, so verify them in the Builda App with real identities.
How do I investigate online Room issues?
The install package includes a separate log diagnostics Skill. When you ask your project's AI to query Room logs, drop statistics, or exit reasons, it uses a read-only diagnostics flow and avoids outputting player private data, credentials, or complete sensitive payloads.