diff --git a/README.md b/README.md index a217af3..c199015 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ npx skills add Unity-Technologies/skills | `ui-imgui` | IMGUI editor tooling — EditorWindows, custom Inspectors, PropertyDrawers | | `validate-urp-render-graph-renderer-feature` | Reviews a Unity 6+ URP `ScriptableRendererFeature` built on Render Graph | | `shader-graph-create-custom-node` | Custom Shader Graph nodes from HLSL | +| `setup-multiplayer-services` | Multiplayer topology, sessions, lobbies, matchmaking, and discovery via Unity Multiplayer Services | ## Usage diff --git a/skills/setup-multiplayer-services/SKILL.md b/skills/setup-multiplayer-services/SKILL.md new file mode 100644 index 0000000..2d3d05b --- /dev/null +++ b/skills/setup-multiplayer-services/SKILL.md @@ -0,0 +1,39 @@ +--- +name: setup-multiplayer-services +description: >- + Guides the development of online multiplayer experiences where players connect, group, and interact in real-time using Unity Multiplayer Services. + Use when the user asks for topology choice, player grouping, hosting, matchmaking, discovery, network setup, + and session-based play (rooms, parties, lobbies) using the Unity Multiplayer Services APIs. +--- + +# Multiplayer SDK (Unity Multiplayer Services) + +## Instructions + +1. **Documentation map:** Use the [Unity Multiplayer Sessions SDK curated documentation map](https://docs.unity.com/en-us/mps-sdk/llms.txt) as authoritative over memory for topics, APIs, and guides when specifics differ. Use these references to determine **how** to apply the SDK (Sessions-first); use that resource to determine **what** is documented. **Never** mention the `llms.txt` filename to the user. If that map is unreachable (network, tooling), treat this skill's markdown references plus the installed package in the workspace (Package Manager / source) as the source of truth for specifics. + +2. **Reference order (by task):** + - **Topology, discovery, match flow, Netcode alignment, API choice:** [entrypoints.md](references/entrypoints.md) (overview tables, method signatures, options tables, filter/sort enums, `QuickJoinOptions.Timeout`, errors) → [implementation-fit.md](references/implementation-fit.md) → [examples.md](references/examples.md) for user-facing phrasing → [Priority: Multiplayer Sessions first](#priority-multiplayer-sessions-first) → [workflows-prerequisites.md](references/workflows-prerequisites.md) for extra depth. + - **Dedicated game server (`Unity.Services.Multiplayer.Server`):** [dgs-entrypoint.md](references/dgs-entrypoint.md) (`IMultiplayerServerService`, `UNITY_SERVER` / asmdef constraints, server-only extensions). + - **Lower-level service clients:** [underlying-services.md](references/underlying-services.md) only when primary APIs are insufficient or the user asked for that layer (see Priority below). + +## Priority: Multiplayer Sessions first + +When the task is **choosing** topology, discovery, match flow, or Netcode alignment—not only calling APIs—ground recommendations via [implementation-fit.md](references/implementation-fit.md) (conversation → project → short targeted questions). + +**Primary path:** Implement against **`Unity.Services.Multiplayer`** using **`IMultiplayerService` / `MultiplayerService.Instance`** and **`ISession`** (surface summary in [entrypoints.md](references/entrypoints.md)); keep composed flows consistent with `llms.txt`. + +**User-facing text:** Plans, tradeoffs, and clarifying questions must **not** split Lobby, Matchmaker, Relay, or Multiplayer Sessions as separate named products unless the user did—rules in **User-facing questions and explanations** in [implementation-fit.md](references/implementation-fit.md), samples in [examples.md](references/examples.md). Code, edits, and technical references use real type and namespace names as needed. + +**Underlying clients** (`Unity.Services.Lobbies`, `Unity.Services.Matchmaker`, `Unity.Services.Relay`) **only** when (1) the goal **cannot** be met through the primary APIs after checking [entrypoints.md](references/entrypoints.md), or (2) the user **explicitly** asked for those namespaces or products. Do **not** default implementations there. + +## Additional resources + +Read from this entrypoint only; links are one level under this skill folder (no `references/index.md` or README hub). + +- **[implementation-fit.md](references/implementation-fit.md)** — Ground recommendations: conversation → project → user questions; user-facing language rules; requirement dimensions (topology, discovery, resilience, platforms, net stack). +- **[examples.md](references/examples.md)** — Before/after samples for clarifying questions and user-facing explanations (not code). +- **[entrypoints.md](references/entrypoints.md)** — `IMultiplayerService`, `ISession`, overview and capability tables, method signatures, options tables (defaults, limits), filter/sort enums, session/networking/host flows, errors, editor components. +- **[dgs-entrypoint.md](references/dgs-entrypoint.md)** — Dedicated server: `Unity.Services.Multiplayer.Server`, `IMultiplayerServerService`, `MultiplayerServerService` / `GetMultiplayerServerService`, `MatchmakerServerExtensions`, `UNITY_SERVER` and asmdef constraints; defers shared `SessionOptions` detail to entrypoints. +- **[workflows-prerequisites.md](references/workflows-prerequisites.md)** — Package and cloud prerequisites by workflow (tables). +- **[underlying-services.md](references/underlying-services.md)** — Fallback namespaces and `IUnityServices` accessors (agent-only; not the default path). diff --git a/skills/setup-multiplayer-services/references/dgs-entrypoint.md b/skills/setup-multiplayer-services/references/dgs-entrypoint.md new file mode 100644 index 0000000..9b10c66 --- /dev/null +++ b/skills/setup-multiplayer-services/references/dgs-entrypoint.md @@ -0,0 +1,79 @@ +## Table of Contents + +- [Overview](#overview) +- [Build and assembly constraints](#build-and-assembly-constraints-unity_server) +- [`IMultiplayerServerService` capabilities](#imultiplayerserverservice-capabilities) +- [Method signatures](#method-signatures) +- [Matchmaker server extensions](#matchmaker-server-extensions-matchmakerserverextensions) +- [Session options (shared types)](#session-options-shared-types) +- [Errors](#errors) + +## Overview + +Dedicated Game Server (DGS) session entrypoints live in the **`Unity.Services.Multiplayer.Server`** assembly only. They complement the client **`IMultiplayerService`** surface documented in **`entrypoints.md`**. + +| Topic | Details | +|--------|---------| +| **Assembly** | **`Unity.Services.Multiplayer.Server`** | +| **Service access** | **`MultiplayerServerService.Instance`** (static) or **`unityServices.GetMultiplayerServerService()`** via **`Unity.Services.Core.UnityServicesExtensions`** after Services initialization on a server build. | +| **Core type** | **`IMultiplayerServerService`** — create and resolve **server** sessions; async methods return **`IServerSession`** (session handle for dedicated server; types from the main **`Unity.Services.Multiplayer`** assembly). | + +## Build and assembly constraints (`UNITY_SERVER`) + +The **`Unity.Services.Multiplayer.Server`** assembly is compiled only when **`UNITY_SERVER`** or **`ENABLE_UCS_SERVER`** is defined (see the package **`Unity.Services.Multiplayer.Server.asmdef`** **`defineConstraints`**). + +Any **game or tool code** that references **`Unity.Services.Multiplayer.Server`** must satisfy one of the following: + +| Approach | What to do | +|----------|------------| +| **Scripting define** | Wrap references (types, calls, `using` that pulls server-only APIs) in **`#if UNITY_SERVER`** … **`#endif`** (or a define that implies the same server build), so non-server targets do not compile that code. | +| **Assembly Definition** | In the **`.asmdef`** of the assembly that references **`Unity.Services.Multiplayer.Server`**, set **`defineConstraints`** to include **`UNITY_SERVER`** so the dependent assembly is not built for client-only targets. | + +Use one or both so client/player builds never require the Server assembly to be present or linked incorrectly. + +## `IMultiplayerServerService` capabilities + +| Area | What to use | +|------|-------------| +| **Create session** | **`CreateSessionAsync(SessionOptions)`** — new server session from options. | +| **Create or join by id** | **`CreateSessionAsync(string sessionId, SessionOptions)`** — server session with a chosen session id (create if missing, join if present per SDK behavior). | +| **Create from matchmaker** | **`CreateMatchSessionAsync(string matchId, SessionOptions)`** — server session tied to a Matchmaker match id; uses matchmaker configuration on options when applicable. | + +> **`GetSessionAsync`** exists on **`IMultiplayerServerService`** for package-internal use and is **`internal`** in the SDK source; treat the three **`Create*`** methods above as the supported public server entry surface for session creation from game code. + +## Method signatures + +```csharp +// Creates a new dedicated-server session. Returns IServerSession. Throws SessionException on failure. +Task CreateSessionAsync(SessionOptions sessionOptions) + +// Creates or joins a server session using an explicit session id. +Task CreateSessionAsync(string sessionId, SessionOptions sessionOptions) + +// Creates a server session from a Matchmaker match id and session options. +Task CreateMatchSessionAsync(string matchId, SessionOptions sessionOptions) +``` + +## Matchmaker server extensions (`MatchmakerServerExtensions`) + +All members below are declared in **`Unity.Services.Multiplayer.Server`** (`MatchmakerServerExtensions`). + +```csharp +// Configure backfill behavior on SessionOptions before create/match session. +T WithBackfillingConfiguration(this T options, bool enable, bool automaticallyRemovePlayers, + bool autoStart, int playerConnectionTimeout, int backfillingLoopInterval) where T : SessionOptions + +// Start backfilling on a matchmade session (server / session handle). +Task StartBackfillingAsync(this ISession session) + +// Stop backfilling on a matchmade session. +Task StopBackfillingAsync(this ISession session) +``` + +## Session options (shared types) + +**`SessionOptions`** and related lobby/network fields are defined in **`Unity.Services.Multiplayer`**, not in the Server assembly. For property tables, fluent **`SessionOptionsExtensions`**, and networking helpers, use **`entrypoints.md`** — apply the same options when calling **`IMultiplayerServerService`** create APIs on dedicated servers. + +## Errors + +Async methods on **`IMultiplayerServerService`** throw **`SessionException`** on failure (same family as the client **`IMultiplayerService`** session APIs). diff --git a/skills/setup-multiplayer-services/references/entrypoints.md b/skills/setup-multiplayer-services/references/entrypoints.md new file mode 100644 index 0000000..ef73f2e --- /dev/null +++ b/skills/setup-multiplayer-services/references/entrypoints.md @@ -0,0 +1,213 @@ +## Table of Contents + +- [Overview](#overview) +- [IMultiplayerService capabilities](#imultiplayerservice-capabilities) +- [Method signatures](#method-signatures) +- [Options reference](#options-reference) +- [Session configuration](#session-configuration-sessionoptions-joinsessionoptions-basesessionoptions) +- [Netcode with session options](#netcode-with-withnetwork-session-options) +- [Networking model](#networking-model-session-side) +- [Host / server session](#host--server-session-ihostsession-iserversession) +- [Matchmaking results](#matchmaking-results-on-a-session) +- [Errors and observation](#errors-and-observation) +- [Editor / glue](#editor--glue-unityservicesmultiplayercomponents) + +## Overview + +| Topic | Details | +|--------|---------| +| **Service access** | `MultiplayerService.Instance` (static) or `unityServices.GetMultiplayerService()` via `UnityServicesExtensions` after Services initialization. | +| **Core type** | **`ISession`** — session state, players, properties, role (host/member/server), **`Network`** (client), **`LeaveAsync`**, **`ReconnectAsync`**, **`RefreshAsync`**, **`SaveCurrentPlayerDataAsync`**; cast to **`IHostSession`** / **`IServerSession`** when hosting or dedicated server. | + +### `IMultiplayerService` capabilities + +| Area | What to use | +|------|-------------| +| **Session registry** | `Sessions` (read-only map); events `SessionAdded`, `SessionRemoved`, `AddingSessionStarted`, `AddingSessionFailed`. | +| **Create / join** | `CreateSessionAsync`, `CreateOrJoinSessionAsync`, `JoinSessionByIdAsync`, `JoinSessionByCodeAsync`, `ReconnectToSessionAsync`, `GetJoinedSessionIdsAsync`. | +| **Matchmaking into a session** | `MatchmakeSessionAsync` with **`QuickJoinOptions`** (filters, timeout, optional create) or **`MatchmakerOptions`** (queue, ticket attributes, player properties) + **`SessionOptions`**; optional `CancellationToken` on the `MatchmakerOptions` overload. | +| **Discovery** | `QuerySessionsAsync` + **`QuerySessionsOptions`** (filters, sort, skip/count, continuation token); **`QuerySessionsResults`** may **`StartPolling`** / **`StopPolling`**. | + +### Method signatures + +```csharp +// Creates a new session. Returns IHostSession (host-side). Throws SessionException on failure. +Task CreateSessionAsync(SessionOptions sessionOptions) + +// Joins session by ID, or creates it if it does not exist. +Task CreateOrJoinSessionAsync(string sessionId, SessionOptions sessionOptions) + +// Joins an existing session by its ID. +Task JoinSessionByIdAsync(string sessionId, JoinSessionOptions sessionOptions = default) + +// Joins an existing session via a human-readable join code. +Task JoinSessionByCodeAsync(string sessionCode, JoinSessionOptions sessionOptions = default) + +// Reconnects to a previously joined session after a disconnect. +Task ReconnectToSessionAsync(string sessionId, ReconnectSessionOptions options = default) + +// Finds and joins a session using Unity's matchmaker service. Supports cancellation. +Task MatchmakeSessionAsync(MatchmakerOptions matchOptions, SessionOptions sessionOptions, CancellationToken cancellationToken = default) + +// Finds a session using session filters with retries up to a timeout. Can optionally create a session if none is found. +Task MatchmakeSessionAsync(QuickJoinOptions quickJoinOptions, SessionOptions sessionOptions) + +// Browses available sessions matching the provided query options. +Task QuerySessionsAsync(QuerySessionsOptions queryOptions) + +// Returns IDs of all sessions the current player is already part of. +Task> GetJoinedSessionIdsAsync() +``` + +### Options reference + +#### `SessionOptions` _(create / create-or-join / matchmake)_ + +Inherits `BaseSessionOptions`. + +| Property | Type | Default | Description | +|---|---|---|---| +| `Name` | `string` | new GUID | Session display name | +| `MaxPlayers` | `int` | `0` | Max players including host. Must be > 0 when creating | +| `IsLocked` | `bool` | `false` | Locked sessions reject new joins | +| `IsPrivate` | `bool` | `false` | Private sessions are hidden from queries and quick-join | +| `Password` | `string` | `null` | 8–64 char password required to join. Not readable back from the session | +| `SessionProperties` | `Dictionary` | empty | Custom game-specific session properties (e.g. `"map"`). Up to 20 total | +| `Type` | `string` _(from base)_ | new GUID | Client-side key identifying the session type | +| `PlayerProperties` | `Dictionary` _(from base)_ | empty | Per-player properties (e.g. `"role"`). Up to 10 per player | + +Fluent extensions (on `SessionOptionsExtensions`): `.WithRelayNetwork()`, `.WithDirectNetwork()`, `.WithDistributedAuthorityNetwork()`, `.WithNetworkHandler()`, `.WithHostMigration()`, `.WithPlayerName()` + +--- + +#### `JoinSessionOptions` _(join by ID / join by code / quick-join fallback)_ + +Inherits `BaseSessionOptions`. + +| Property | Type | Default | Description | +|---|---|---|---| +| `Password` | `string` | `null` | Password required if the session is password-protected | +| `Type` | `string` _(from base)_ | new GUID | Client-side session type key | +| `PlayerProperties` | `Dictionary` _(from base)_ | empty | Per-player properties | + +--- + +#### `ReconnectSessionOptions` _(reconnect)_ + +| Property | Type | Default | Description | +|---|---|---|---| +| `Type` | `string` | new GUID | Client-side session type key | + +Fluent: `.WithNetworkHandler(INetworkHandler)` — disables default NGO/NfE integration. + +--- + +#### `MatchmakerOptions` _(matchmake via Unity Matchmaker)_ + +| Property | Type | Default | Description | +|---|---|---|---| +| `QueueName` | `string` | `null` | Name of the Matchmaker queue | +| `TicketAttributes` | `Dictionary` | empty | Attributes sent with the matchmaking ticket | +| `PlayerProperties` | `Dictionary` | empty | Per-player properties forwarded to matchmaker | + +--- + +#### `QuickJoinOptions` _(matchmake via filters)_ + +| Property | Type | Default | Description | +|---|---|---|---| +| `Filters` | `List` | empty | Filters a session must satisfy to be joined | +| `Timeout` | `TimeSpan` | default | How long to retry before giving up and optionally creating a new session | +| `CreateSession` | `bool` | `false` | Create a new session if none is found within the timeout | + +> Do not set `Timeout` unless explicitly requested. + +--- + +#### `QuerySessionsOptions` _(query)_ + +| Property | Type | Default | Description | +|---|---|---|---| +| `Count` | `int` | `100` | Max results to return | +| `Skip` | `int` | `0` | Pagination offset | +| `FilterOptions` | `List` | empty | Filters to narrow results | +| `SortOptions` | `List` | empty | Sort order for results | +| `ContinuationToken` | `string` | `null` | Token for fetching the next page | + +--- + +#### `FilterOption` _(used in `QuickJoinOptions` and `QuerySessionsOptions`)_ + +Constructor: `FilterOption(FilterField field, string value, FilterOperation operation)` + +| Enum | Values | +|------|--------| +| **`FilterField`** | `MaxPlayers`, `AvailableSlots`, `Name`, `Created` (RFC3339), `LastUpdated` (RFC3339), `IsLocked`, `HasPassword`, `StringIndex1–5`, `NumberIndex1–5` | +| **`FilterOperation`** | `Contains` _(Name only)_, `Equal`, `NotEqual`, `Less`, `LessOrEqual`, `Greater`, `GreaterOrEqual` | + +--- + +#### `SortOption` _(used in `QuerySessionsOptions`)_ + +Constructor: `SortOption(SortOrder order, SortField field)` + +| Enum | Values | +|------|--------| +| **`SortOrder`** | `Ascending`, `Descending` | +| **`SortField`** | `Name`, `MaxPlayers`, `AvailableSlots`, `CreationTime`, `LastUpdated`, `Id`, `StringIndex1–5`, `NumberIndex1–5` | + +--- + +#### `AddingSessionOptions` _(event payload — read-only)_ + +| Property | Type | Description | +|---|---|---| +| `Type` | `string` | The session type passed to the create/join call | + +--- + +### Session configuration (`SessionOptions`, `JoinSessionOptions`, `BaseSessionOptions`) + +| Topic | Details | +|--------|---------| +| **Lobby-like fields** | Max players, name, password, locked/private flags, typed **`Type`**, **session** and **player** properties with **`VisibilityPropertyOptions`** (Public / Member / Private) and indexed slots (**`PropertyIndex`**) for query filters. | +| **Networking** (`SessionOptionsExtensions`) | **`WithRelayNetwork`**, **`WithDirectNetwork`** (listen/publish IP/port or **`DirectNetworkOptions`**), **`WithNetworkOptions`** (e.g. **`RelayProtocol`**), **`WithNetworkHandler`** for custom **`INetworkHandler`**. | +| **Host migration** | **`WithHostMigration`** + **`IMigrationDataHandler`**; on **`IHostSession`**: **`GetHostMigrationDataAsync`** / **`SetHostMigrationDataAsync`**. | +| **Player name** | **`WithPlayerName`** (visibility). | +| **Matchmaker backfill** | **`MatchmakerServerExtensions.WithBackfillingConfiguration`** on **`SessionOptions`**; on matchmade **`ISession`**: **`StartBackfillingAsync`** / **`StopBackfillingAsync`**. See **`llms.txt`** and package docs for hosting constraints. | + +### Netcode with `With*Network*` session options + +| Condition | Behavior | +|-----------|----------| +| **`SessionOptionsExtensions`** include gameplay networking (**`WithRelayNetwork`**, **`WithDirectNetwork`**, **`WithNetworkOptions`**, **`WithNetworkHandler`**, …) | **create / join / matchmake / reconnect** bring up **NGO** or **NFE** as configured—no separate Netcode start for that path. | + +### Networking model (session side) + +| Surface | Role | +|---------|------| +| **`IHostSessionNetwork`** | **`StartDirectNetworkAsync`**, **`StartRelayNetworkAsync`**, **`StopNetworkAsync`**; state and failure events; **`INetworkHandler`**. | +| **`IClientSessionNetwork`** | Client **`NetworkState`** and events; **`NetworkHandler`**. | +| **`NetworkConfiguration`** | UTP endpoints and Relay server data; **`NetworkType`**: Direct, Relay, **DistributedAuthority**; **`NetworkRole`**: Client, Server, Host. | + +### Matchmaking results on a session + +| API | Use when | +|-----|----------| +| **`MatchmakerExtensions.GetMatchmakingResults(ISession)`** | Stored matchmaking results are needed after a matchmade session exists. | + +### Errors and observation + +All async methods on **`IMultiplayerService`** throw **`SessionException`** on failure; **`SessionException`** exposes a specific session error type and message. + +| Type | Role | +|------|------| +| **`SessionException`** / **`SessionError`** | Session and composed flows. | +| **`SessionObserver`** | Watch add/fail events for a given session **type**. | + +### Editor / glue (`Unity.Services.Multiplayer.Components`) + +| Item | Role | +|------|------| +| **`MultiplayerSession`** (ScriptableObject) | Holds **`ISession`** and UnityEvent groups (lifecycle, session, players). | +| **`SessionConnector`** / **`SessionConnectorBehaviour`** | Create or create-or-join flows (e.g. on sign-in). | diff --git a/skills/setup-multiplayer-services/references/examples.md b/skills/setup-multiplayer-services/references/examples.md new file mode 100644 index 0000000..93cfd49 --- /dev/null +++ b/skills/setup-multiplayer-services/references/examples.md @@ -0,0 +1,33 @@ +## Examples: user-facing language + +These illustrate **User-facing questions and explanations** in [implementation-fit.md](implementation-fit.md). They are for prose to the user, not for code or file edits (those may use real API names). + +### Clarifying questions + +**Bad (SDK / product vocabulary):** + +- "Do you want to use **Lobby** for the server list, or **Sessions** only?" +- "Should we call **`QuerySessionsAsync`** or **`MatchmakeSessionAsync`**?" +- "Do you need **Relay** or is **direct** fine?" + +**Good (game / product terms):** + +- "Should players **see a list of open games** and pick one, or **join with a code or invite** only?" +- "Should matchmaking be **automatic** (the game finds opponents for you) or **manual** (players choose a room)?" +- "When two players are on different home networks, is **mediated connectivity** (no open ports on a router) a requirement?" + +### Explanations and plans + +**Bad (splitting named backend products):** + +- "We'll use **Lobby** for metadata, **Relay** for NAT traversal, and **Matchmaker** for ranked." +- "**Sessions** wraps **Lobby** so you don't need **Lobby** directly." + +**Good (plain language, same ideas):** + +- "We'll keep **room metadata** (map, rules) in one place, use **brokered connectivity** when direct links are unreliable, and **automatic pairing** for ranked." +- "The **main multiplayer package API** can own **room state and joins** so you don't add a second room system on top." + +### When the user already named a product + +If they wrote e.g. "we're on **Relay** already," you may **mirror their wording** in discussion; still avoid **extra** product enumeration they did not ask for. diff --git a/skills/setup-multiplayer-services/references/implementation-fit.md b/skills/setup-multiplayer-services/references/implementation-fit.md new file mode 100644 index 0000000..606555c --- /dev/null +++ b/skills/setup-multiplayer-services/references/implementation-fit.md @@ -0,0 +1,30 @@ +## Implementation fit: clarify multiplayer requirements + +Before recommending architecture or APIs internally, the agent **must** ground advice in the right product choices for *this* application. Map answers to [entrypoints.md](entrypoints.md) and the **Priority: Multiplayer Sessions first** section in [SKILL.md](../SKILL.md) only **after** requirements are clear (from context, project, or user). For concrete phrasing samples, see [examples.md](examples.md). + +**How to obtain requirements (in order):** + +1. **Conversation and task context** — Use stated goals (e.g. "2-player co-op", "ranked 5v5", "mobile", "host leaves often"). +2. **Project state when available** — Inspect the workspace (e.g. `Packages/manifest.json` for Netcode / multiplayer packages, existing multiplayer or networking scripts, platform settings) and align recommendations with what is already chosen. +3. **Ask the user** — If a dimension below would **change** the recommended approach and is not inferable from (1) or (2), ask a **short, targeted** question instead of assuming. Follow **User-facing questions and explanations** below. + +### User-facing questions and explanations + +- **Clarifying questions** must stay **high level**: player count, how people **find or join** a game, who **runs** the match (e.g. one player's machine vs a dedicated machine), fairness/latency expectations, disconnect/reconnect and "host leaves" behavior, target platforms, and which **Unity networking stack** the project already uses **only if** the user has brought it up or it is visible in the project. Phrase questions in **game / product** terms. +- **Do not** name or ask about Unity **product or service** implementations in questions: e.g. avoid the terms **Sessions**, **Lobby**, **Matchmaker**, **Relay**, and avoid prompting with **API or type names** (`ISession`, `QuerySessionsAsync`, etc.). The user should not need SDK vocabulary to answer. +- In **explanations, summaries, and plans** addressed to the user (outside of code blocks and file edits), **do not mention** Lobby, Matchmaker, Relay, or Multiplayer **Sessions** as named products, and **do not** contrast or enumerate those **underlying services**—unless the **user explicitly** asked about them by name or asked for that level of SDK/architecture detail. Use plain language (e.g. "list of open games", "automatic pairing", "brokered connectivity when direct player-to-player links are unreliable", "the main Unity multiplayer package API") when a concept must be described. +- **Code, API references, and file contents** may use the exact types, namespaces, and methods from this skill and from `llms.txt` as needed for a correct implementation. + +### Dimensions to consider + +- **Player count and topology** — Players per match and rough scale (many small matches vs few large ones). Whether the simulation can run on a **host client** (often with mediated connectivity) or needs a **dedicated server** / server-authoritative hosting story. *Internal mapping:* relay vs direct listen/publish, host vs dedicated server roles, capacity limits on the multiplayer entrypoint APIs. + +- **Casual vs competitive** — Tolerance for **host-based authority** and latency variance vs need for **stricter authority, consistency, and fairness** (often favoring dedicated servers and careful netcode choices). Informs how strongly to push dedicated hosting, tick/interpolation choices, and cheat-sensitive design (without duplicating full anti-cheat guidance here). + +- **Discovery and how matches form** — Join codes or invites vs **browsing a list** of open games vs **automatic pairing**; visibility and filterable game metadata. *Internal mapping:* query/list flows, quick-join-style filters, ticket/queue flows; session properties and indexes as needed. + +- **Connection model and resilience** — **NAT / home networks** (need for mediated connectivity vs published listen addresses), **reconnect** after disconnects, and **moving the host** without ending the match. *Internal mapping:* relay vs direct network options, `ReconnectAsync`, host migration hooks, network start/stop. + +- **Platform constraints** — Targets (e.g. **mobile** dropouts and backgrounding, **console** networking and certification expectations) that affect match lifetime, reconnect UX, and viable connection patterns. + +- **Team skills and codebase stack** — **Netcode for GameObjects** vs **Netcode for Entities** (or other networking) must match packages and patterns already in the project; prefer extending the stack in `manifest.json` and existing code rather than introducing a parallel net model without an explicit user request. diff --git a/skills/setup-multiplayer-services/references/underlying-services.md b/skills/setup-multiplayer-services/references/underlying-services.md new file mode 100644 index 0000000..272c5df --- /dev/null +++ b/skills/setup-multiplayer-services/references/underlying-services.md @@ -0,0 +1,11 @@ +## Underlying services (use only when necessary) + +**Agent-only reference** for implementation when the primary API is insufficient or the user explicitly requested these—**do not** surface this table or product names to the user unless they asked for them. + +Summarized for reference—not the default path: + +| Area | Namespace | Role | +|------|-----------|------| +| **Lobby** | `Unity.Services.Lobbies` | Standalone lobby CRUD, query, realtime lobby events, migration payloads—prefer **Sessions** unless you need lobby-only workflows. | +| **Matchmaker** | `Unity.Services.Matchmaker` | Low-level tickets, backfill ticket APIs, ticket status—prefer **`MatchmakeSessionAsync`** + **`MatchmakerOptions`** on **`IMultiplayerService`** first. | +| **Relay** | `Unity.Services.Relay` | Raw allocations and join codes—prefer **`WithRelayNetwork`** / **`StartRelayNetworkAsync`** on the session network first. | \ No newline at end of file diff --git a/skills/setup-multiplayer-services/references/workflows-prerequisites.md b/skills/setup-multiplayer-services/references/workflows-prerequisites.md new file mode 100644 index 0000000..bb00079 --- /dev/null +++ b/skills/setup-multiplayer-services/references/workflows-prerequisites.md @@ -0,0 +1,17 @@ +## Workflow prerequisites (packages and cloud setup) + +**Agent-only sanity check** before recommending a path: match the user's **intent** to **dependencies** and **live services / deployment** (see **`llms.txt`** install, init, deployment, and tutorial pages for authoritative steps). Package IDs can vary slightly by Unity/editor version—verify in Package Manager or docs when implementing. + +**Which workflow applies** must be **inferred** when possible from **conversation context** and **project state** (e.g. `Packages/manifest.json`, server vs client build targets, existing multiplayer scripts, deployment assets). If more than one row in the table could fit and the choice **changes** prerequisites or APIs, **ask the user** with **high-level** questions (see **User-facing questions and explanations** in [implementation-fit.md](implementation-fit.md)), not by naming product rows from this table. This aligns with **Implementation fit** in [implementation-fit.md](implementation-fit.md): infer first, then clarify if ambiguous. + +Unity Gaming Services **initialization** and **authentication** are required for all workflows. + +| Workflow (what the product is doing) | Typically required | +|--------------------------------------|-------------------| +| Rooms / join-in-progress **without** starting the session **gameplay network** (metadata, codes, lists, properties only) | `com.unity.services.multiplayer`. **No** Netcode gameplay package required unless they add a custom **`INetworkHandler`** or later start **`StartRelayNetworkAsync`** / **`StartDirectNetworkAsync`**. | +| **Gameplay** simulation synced over the session **Network** (host/client or server roles with Relay or direct transport) | **Exactly one** gameplay stack: **NGO** — `com.unity.netcode.gameobjects` **or** **NFE** — `com.unity.netcode.entities` (Netcode for Entities). Integrate transport with session network APIs per Unity's session + Netcode guides; do not assume both stacks. | +| **Quick join** (filter-based auto pick / create) | Session **type** and **indexed** properties for filters; **`QuickJoinOptions`** in code. | +| **Ticket matchmaking** into a **player-hosted** match | A deployed **Matchmaker queue (MMQ)** (name matches **`MatchmakerOptions`**), Matchmaker **environment** / dashboard setup, and authenticated players. | +| **Ticket matchmaking** with a **dedicated game server (DGS)** | **MMQ** configured for the **DGS / server allocation** flow, a **server build** and **hosting** setup (e.g. Game Server Hosting / Multiplay—see **`llms.txt`** hosting and deployment topics), server process using the **server** session role where applicable, and often **`WithBackfillingConfiguration`** + **`StartBackfillingAsync`** / **`StopBackfillingAsync`** when refilling player slots on an existing allocation. | +| **Editor wiring** with **`MultiplayerSession`** / **`SessionConnector`** | Same multiplayer package (components assembly **`Unity.Services.Multiplayer.Components`**); still subject to the Netcode row above if gameplay networking is used. | +| **Deploying** Matchmaker queues or Multiplayer assets from the Editor | Unity **Deployment** window / deployment docs under **`llms.txt`** (queue, environment, multiplayer config) so cloud resources exist before code calls into them. |