### Running the Chat Example Source: https://playmesh.dev/docs/examples Instructions for setting up and running the chat server and clients. Includes installation and execution commands for multiple terminals. ```bash cd examples/chat && npm install # Terminal 1 npx tsx server.ts # Terminal 2 npx tsx client.ts alice # Terminal 3 npx tsx client.ts bob ``` -------------------------------- ### Install PlayMesh Server Package Source: https://playmesh.dev/docs/getting-started Install the server package using npm. Socket.IO, ioredis, and BullMQ are bundled and do not require separate installation. ```bash npm install @playmesh/server ``` -------------------------------- ### Full Example Source: https://playmesh.dev/docs/client-sdk A comprehensive example demonstrating the integration of PlayMeshClient in a game client, including connection, event handling, and sending commands. ```APIDOC ## Full example ◆game-client.ts ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient({ url: import.meta.env.VITE_SERVER_URL, auth: async () => ({ token: await authService.getAccessToken() }), }); client.on('world-state', payload => gameRenderer.updateWorld(payload)); client.on('player-joined', payload => gameRenderer.spawnPlayer(payload.userId)); client.on('player-left', payload => gameRenderer.removePlayer(payload.userId)); client.onDisconnect(() => gameRenderer.showDisconnectedOverlay()); client.onReconnect(() => gameRenderer.hideDisconnectedOverlay()); export async function startGame() { const session = await client.connect(); gameRenderer.setLocalPlayerId(session.userId); } export const movePlayer = (x: number, y: number) => client.emit('move', { x, y }); export const sendChat = (message: string) => client.emit('chat', { message }); ``` ``` -------------------------------- ### Install PlayMesh Server and Client Source: https://playmesh.dev Install the necessary PlayMesh packages for both server and client development using npm. ```bash $npm install @playmesh/server @playmesh/client ``` -------------------------------- ### Import PlayMesh and Initialize Server Source: https://playmesh.dev/docs/server-sdk Import the PlayMesh class and initialize a new PlayMesh instance with optional configuration. The server can be configured with a port, Redis connection details, and Socket.IO options. Use the bootstrap hook for asynchronous setup before the server starts accepting connections. ```typescript import { PlayMesh } from '@playmesh/server'; const mesh = new PlayMesh(options?: PlayMeshOptions); ``` ```typescript const mesh = new PlayMesh({ port: 4000, redis: { host: 'localhost', port: 6379 } }); mesh.bootstrap(async ({ mesh }) => { /* create domains and instances */ }); mesh.onAuthenticate(async request => ({ userId: request.auth.userId as string })); mesh.onStarted(() => console.log('Server ready')); const { port } = await mesh.start(); process.on('SIGTERM', () => mesh.shutdown()); ``` -------------------------------- ### Full Game Client Example Source: https://playmesh.dev/docs/client-sdk A comprehensive example demonstrating client initialization, event subscriptions for game state updates, connection lifecycle management, and emitting player actions. This serves as a template for integrating the client into a game. ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient({ url: import.meta.env.VITE_SERVER_URL, auth: async () => ({ token: await authService.getAccessToken() }), }); client.on('world-state', payload => gameRenderer.updateWorld(payload)); client.on('player-joined', payload => gameRenderer.spawnPlayer(payload.userId)); client.on('player-left', payload => gameRenderer.removePlayer(payload.userId)); client.onDisconnect(() => gameRenderer.showDisconnectedOverlay()); client.onReconnect(() => gameRenderer.hideDisconnectedOverlay()); export async function startGame() { const session = await client.connect(); gameRenderer.setLocalPlayerId(session.userId); } export const movePlayer = (x: number, y: number) => client.emit('move', { x, y }); export const sendChat = (message: string) => client.emit('chat', { message }); ``` -------------------------------- ### Install PlayMesh Client Package Source: https://playmesh.dev/docs/getting-started Install the client package for your frontend or game client project using npm. ```bash npm install @playmesh/client ``` -------------------------------- ### Start PlayMesh Instance Source: https://playmesh.dev/docs/architecture Initialize and start a PlayMesh instance. This is the root of a PlayMesh deployment, managing domains, sessions, authentication, and connections. ```javascript const mesh = new PlayMesh({ port: 4000 }); await mesh.start(); ``` -------------------------------- ### Authentication Examples Source: https://playmesh.dev/docs/client-sdk Configure authentication using a static token or a dynamic function for token refresh. ```APIDOC ## Authentication ```typescript // Static token const client = new PlayMeshClient({ url: 'https://game.example.com', auth: { token: localStorage.getItem('jwt') }, }); // Dynamic — refreshed on each reconnect const client = new PlayMeshClient({ url: 'https://game.example.com', auth: async () => ({ token: await authService.getToken() }), }); ``` ``` -------------------------------- ### Client Authentication Options Source: https://playmesh.dev/docs/auth-admission Examples of how to provide authentication credentials when initializing a PlayMesh client. This can be a static token or a dynamic function that retrieves a token. ```javascript // Static token const client = new PlayMeshClient({ url: 'https://game.example.com', auth: { token: 'eyJhbGci...' }, }); // Dynamic — refreshed on each reconnect const client = new PlayMeshClient({ url: 'https://game.example.com', auth: async () => ({ token: await authService.getToken() }), }); ``` -------------------------------- ### Connect a Client to the PlayMesh Chat Server Source: https://playmesh.dev This example shows how to connect a client to the PlayMesh server, handle incoming chat messages, and send a message. ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient({ url: 'http://localhost:4000', auth: { username: 'alice' }, }); client.on('chat', payload => { console.log( `${payload.sender}: ${payload.message}` ); }); const session = await client.connect(); console.log('Joined:', session.instances); client.emit('chat', { message: 'Hello, world!', }); ``` -------------------------------- ### Using a Plugin Source: https://playmesh.dev/docs/middleware-plugins Demonstrates how to instantiate and use a custom plugin with PlayMesh. The plugin is passed to the mesh.use() method before starting the server. ```typescript const mesh = new PlayMesh({ port: 4000 }); mesh.use(new MatchmakingPlugin(2)); await mesh.start(); ``` -------------------------------- ### Create a Multiplayer Chat Server with PlayMesh Source: https://playmesh.dev This example demonstrates how to set up a basic multiplayer chat server using PlayMesh. It includes authentication, domain and instance creation, and broadcasting chat messages. ```typescript import { PlayMesh } from '@playmesh/server'; const mesh = new PlayMesh({ port: 4000 }); mesh.onAuthenticate(async req => ({ userId: String(req.auth.username), })); mesh.bootstrap(async ({ mesh }) => { const lobby = mesh .createDomain('social') .createInstance('lobby'); lobby.on('chat', (session, payload) => { lobby.broadcast('chat', { sender: session.userId, message: payload.message, }); }); }); mesh.onAdmission(async () => ({ instances: ['social/lobby'], })); await mesh.start(); ``` -------------------------------- ### Set and Get Instance State Source: https://playmesh.dev/docs/instance-state Use `set` to store JSON-serializable values and `get` to retrieve them. The `get` method returns `undefined` if the key is not found. Ensure values are correctly typed when retrieved. ```javascript await dungeon.state.set('boss-health', 5000); dungeon.on('attack-boss', async (session, payload) => { const { damage } = payload as { damage: number }; const health = (await dungeon.state.get('boss-health') as number) ?? 0; const newHealth = Math.max(0, health - damage); await dungeon.state.set('boss-health', newHealth); dungeon.broadcast('boss-health-updated', { health: newHealth }); if (newHealth === 0) { dungeon.broadcast('boss-defeated', { killer: session.userId }); } }); ``` ```javascript match.onJoin(async () => { if (match.sessions.length === 2) { await match.state.set('started-at', Date.now()); match.broadcast('match-started', { timeLimitMs: 5 * 60 * 1000 }); } }); ``` ```javascript dungeon.on('monster-killed', async (session) => { const kills = ((await dungeon.state.get('kills') as number) ?? 0) + 1; await dungeon.state.set('kills', kills); dungeon.broadcast('kill-count', { kills }); }); ``` -------------------------------- ### Writing a Matchmaking Plugin Source: https://playmesh.dev/docs/middleware-plugins An example of a plugin that implements matchmaking logic. It creates domains and instances, manages a player queue, and forms matches when enough players are available. ```typescript import type { PlayMesh, Plugin } from '@playmesh/server'; class MatchmakingPlugin implements Plugin { readonly name = 'matchmaking'; private queue: string[] = []; constructor(private readonly minPlayers: number) {} async install(mesh: PlayMesh) { const domain = mesh.createDomain('ranked'); const lobby = domain.createInstance('lobby'); mesh.onSessionCreate(async session => { this.queue.push(session.id); if (this.queue.length >= this.minPlayers) { const players = this.queue.splice(0, this.minPlayers); const match = domain.createInstance(`match-${Date.now()}`); for (const id of players) { const s = mesh.sessions.find(s => s.id === id); if (s) { await s.leave(lobby); await s.join(match); } } } }); mesh.onDisconnect(session => { this.queue = this.queue.filter(id => id !== session.id); }); } } ``` -------------------------------- ### Multi-Zone World Setup Source: https://playmesh.dev/docs/examples Sets up multiple zones within a 'world' domain, handling player joins, leaves, and chat within each zone. It also manages player travel between zones and persistence of their current zone. ```typescript const ZONES = ['forest', 'village', 'dungeon', 'market']; mesh.bootstrap(async ({ mesh }) => { const world = mesh.createDomain('world'); for (const zone of ZONES) { const instance = world.createInstance(zone); instance.onJoin(session => { instance.broadcast('player-entered', { userId: session.userId, zone }); }); instance.onLeave(session => { instance.broadcast('player-left', { userId: session.userId, zone }); }); instance.on('chat', (session, payload) => { instance.broadcast('chat', { zone, sender: session.userId, ...payload }); }); } }); mesh.onAdmission(async request => { const player = await db.players.findById(request.userId); return { instances: [`world/${player.currentZone ?? 'village'}`] }; }); mesh.onStarted(() => { const world = mesh.domain('world'); for (const instance of world.instances) { instance.on('travel-to', async (session, payload) => { const { zone } = payload as { zone: string }; if (!ZONES.includes(zone)) return; for (const current of session.instances) { if (current.domain === world) await session.leave(current); } await session.join(world.instance(zone)); await db.players.setZone(session.userId, zone); }); } }); ``` -------------------------------- ### Environment Variables for PlayMesh Source: https://playmesh.dev/docs/deployment Example .env file for configuring PlayMesh server settings including port, Redis URL, client origin, and JWT secret. ```dotenv PORT=4000 REDIS_URL=redis://localhost:6379 CLIENT_ORIGIN=https://game.example.com JWT_SECRET=your-secret-here ``` -------------------------------- ### Messaging and Extensions Source: https://playmesh.dev/docs/server-sdk Utilities for broadcasting messages, applying middleware, and installing plugins. ```APIDOC ## Messaging and Extensions ### Description Provides methods for broadcasting messages to all connected clients, applying middleware functions to process requests, and installing external plugins. ### Methods #### `broadcast(event: string, payload?: any)` - **Description**: Sends a specified event with optional payload to every connected session across all nodes in a distributed setup. - **Parameters**: - `event` (string): The name of the event to broadcast. - `payload` (any): Optional data to send with the event. - **Returns**: `void` #### `use(middleware: Function)` - **Description**: Registers a middleware function to be executed for incoming requests. Middleware can modify requests or responses. - **Parameters**: - `middleware` (Function): The middleware function to register. - **Returns**: `this` #### `use(plugin: { install: (mesh: PlayMesh) => void })` - **Description**: Installs a plugin that must have an `install` method. The `install` method receives the PlayMesh instance. - **Parameters**: - `plugin` (object): The plugin object with an `install` method. - **Returns**: `this` ``` -------------------------------- ### Plugin Interface Source: https://playmesh.dev/docs/middleware-plugins Defines the structure of a plugin object. Plugins must have an optional 'name' and an 'install' method that receives the PlayMesh instance. ```typescript interface Plugin { name?: string; install(mesh: PlayMesh): void | Promise; } ``` -------------------------------- ### Dockerfile for PlayMesh Production Build Source: https://playmesh.dev/docs/deployment Dockerfile to build a production-ready PlayMesh server image. Installs dependencies, copies code, builds the project, and sets production environment variables. ```dockerfile FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . RUN npm run build ENV NODE_ENV=production PORT=4000 EXPOSE 4000 CMD ["node", "dist/server.js"] ``` -------------------------------- ### ScopedState Methods Source: https://playmesh.dev/docs/server-sdk Allows for managing key-value data within the scope of an instance, with support for getting, setting, deleting, and clearing keys. ```APIDOC ## ScopedState Object ### Methods - **get(key)** (Promise) - Get a value. Returns undefined if not set. - **set(key, value)** (Promise) - Set a value (JSON-serialized). - **delete(key)** (Promise) - Delete a key. - **keys()** (Promise) - List all keys in this instance scope. - **clear()** (Promise) - Delete all keys in this instance scope. ``` -------------------------------- ### JWT Authentication with onAuthenticate Source: https://playmesh.dev/docs/auth-admission This example demonstrates using JSON Web Tokens (JWT) for authentication within the `onAuthenticate` hook. Ensure the JWT secret is securely stored in the environment. ```javascript import jwt from 'jsonwebtoken'; mesh.onAuthenticate(async request => { const token = request.token; if (!token) throw new Error('Token required'); const payload = jwt.verify(token, process.env.JWT_SECRET!) as { sub: string; role: string }; return { userId: payload.sub, data: { role: payload.role } }; }); ``` -------------------------------- ### Logging Middleware Source: https://playmesh.dev/docs/middleware-plugins A middleware example that logs the duration of event processing. It calls next() to continue the event flow and logs a warning for slow events. ```typescript mesh.use(async (context, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; if (ms > 100) console.warn(`Slow event: ${context.event} — ${ms}ms`); }); ``` -------------------------------- ### Configure PlayMesh with Redis Source: https://playmesh.dev/docs/getting-started Examples of configuring the PlayMesh server to use Redis for distributed mode, either via a connection object or a connection string. Redis is required for multi-node synchronization and background queues. ```typescript const mesh = new PlayMesh({ port: 4000, redis: { host: 'localhost', port: 6379 }, }); // Or via a connection string: const mesh = new PlayMesh({ port: 4000, redis: 'redis://localhost:6379', }); ``` -------------------------------- ### Get Server Metrics Source: https://playmesh.dev/docs/server-sdk Retrieve active session counts, domain counts, instance counts, and server uptime. This is useful for monitoring server health and load. ```typescript const m = mesh.metrics(); // { // sessions: number, — active sessions on this node // domains: number, // instances: number, // uptimeMs: number, // } ``` -------------------------------- ### Add jobs to a queue Source: https://playmesh.dev/docs/queues Get a queue instance and add a job to it. The `count()` method can be used to check the current number of jobs in the queue. ```typescript const matchmakingQueue = mesh.queues.queue('matchmaking'); lobby.on('join-queue', async (session, payload) => { await matchmakingQueue.add('find-match', { userId: session.userId, rank: (payload as { rank: number }).rank, }); session.send('queued', { position: await matchmakingQueue.count() }); }); ``` -------------------------------- ### Attach PlayMesh to Existing HTTP Server Source: https://playmesh.dev/docs/deployment Integrate PlayMesh with an existing Express.js HTTP server. Initializes PlayMesh with the provided server instance and starts it, allowing PlayMesh to handle WebSocket connections alongside existing routes. ```typescript import express from 'express'; import { createServer } from 'node:http'; import { PlayMesh } from '@playmesh/server'; const app = express(); const httpServer = createServer(app); app.get('/health', (_req, res) => res.json({ ok: true })); app.get('/metrics', (_req, res) => res.json(mesh.metrics())); const mesh = new PlayMesh({ server: httpServer, port: 4000 }); await mesh.start(); ``` -------------------------------- ### Matchmaking with BullMQ Queue Source: https://playmesh.dev/docs/examples This snippet demonstrates how to set up a matchmaking queue using PlayMesh and BullMQ. It requires Redis and illustrates the pattern for adding users to a matchmaking queue and processing jobs to form matches. For production, a skill-based algorithm and dedicated service are recommended. ```typescript const mesh = new PlayMesh({ port: 4000, redis: { host: 'localhost', port: 6379 }, }); mesh.bootstrap(async ({ mesh }) => { const ranked = mesh.createDomain('ranked'); const lobby = ranked.createInstance('lobby'); lobby.on('join-queue', async (session, payload) => { await mesh.queues.queue('matchmaking').add('find-match', { userId: session.userId, rank: (payload as { rank: number }).rank, }); session.send('queued', {}); }); }); mesh.onStarted(() => { mesh.queues.worker('matchmaking', async job => { const { userId, rank } = job.data; const opponent = await matchmakingService.findOpponent(userId, rank); if (!opponent) return; const ranked = mesh.domain('ranked'); const match = ranked.createInstance(`match-${Date.now()}`); for (const uid of [userId, opponent.userId]) { const s = mesh.sessions.find(s => s.userId === uid); if (s) { await s.leave(ranked.instance('lobby')); await s.join(match); } } match.broadcast('match-started', { players: [userId, opponent.userId] }); }); }); await mesh.start(); ``` -------------------------------- ### Import and Initialize PlayMeshClient Source: https://playmesh.dev/docs/client-sdk Import the PlayMeshClient class and create a new instance with options. The options include the server URL and authentication details. ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient(options: PlayMeshClientOptions); ``` -------------------------------- ### Run Server and Client Source: https://playmesh.dev/docs/getting-started Instructions for running the PlayMesh server and client applications using npx and tsx in separate terminal windows. ```bash # Terminal 1 npx tsx server.ts # Terminal 2 npx tsx client.ts # Terminal 3 (second player) npx tsx client.ts ``` -------------------------------- ### Initialize PlayMesh Server with Redis Source: https://playmesh.dev/docs/deployment Instantiate the PlayMesh server, configuring the port, Redis connection URL, and CORS settings for socket connections. Uses environment variables for configuration. ```typescript import { PlayMesh } from '@playmesh/server'; const mesh = new PlayMesh({ port: Number(process.env.PORT ?? 3000), redis: process.env.REDIS_URL ?? undefined, socket: { cors: { origin: process.env.CLIENT_ORIGIN ?? '*', methods: ['GET', 'POST'], }, }, }); ``` -------------------------------- ### PlayMesh Constructor Source: https://playmesh.dev/docs/server-sdk Initialize the PlayMesh server with optional configuration for port, Redis connection, and Socket.IO options. ```APIDOC ## PlayMesh Constructor ### Description Initializes the PlayMesh server. You can configure the port, attach to a pre-existing HTTP server, set up Redis for distributed mode, and forward options to the Socket.IO server. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Method `new PlayMesh(options?: PlayMeshOptions)` ### Parameters - **options** (PlayMeshOptions) - Optional configuration object. - **port** (number) - Port to listen on. Defaults to 3000. Pass 0 for ephemeral port. - **server** (http.Server) - Attach to a pre-existing HTTP server. - **redis** (RedisOptions | string) - Redis connection options or URL string. Enables distributed mode. - **socket** (Partial) - Options forwarded to the Socket.IO server. ### Returns - `PlayMesh` - An instance of the PlayMesh server. ``` -------------------------------- ### Create Instance and Handle Events Source: https://playmesh.dev/docs/architecture Create an isolated multiplayer environment (Instance) within a domain. This snippet shows how to handle session events and broadcast messages within the instance. ```javascript const city = world.createInstance('city-center'); city.on('chat', (session, payload) => { city.broadcast('chat', { sender: session.userId, ...payload }); }); // Synchronized state scoped to this instance await city.state.set('player-count', city.sessions.length); // Count across all nodes const total = await city.memberCount(); ``` -------------------------------- ### PlayMeshClient Constructor Source: https://playmesh.dev/docs/client-sdk Import and instantiate the PlayMeshClient. The constructor accepts an options object for configuring the server URL, authentication, and Socket.IO options. ```APIDOC ## Constructor ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient(options: PlayMeshClientOptions); ``` ### Options - `url` (string): Server URL. e.g. "https://game.example.com" - `auth` (object | () => object): Auth payload for the server's authenticate hook, or a function called on every connection attempt (useful for token refresh). - `socket` (Partial): Options forwarded to the underlying Socket.IO client. ``` -------------------------------- ### Infrastructure and Lifecycle Source: https://playmesh.dev/docs/server-sdk Access core server components and manage the server's lifecycle. ```APIDOC ## Infrastructure and Lifecycle ### Description Provides access to the underlying Socket.IO server, Redis client, and QueueManager, along with methods to start, stop, and retrieve metrics for the PlayMesh server. ### Methods #### `start()` - **Description**: Starts the PlayMesh server, making it ready to accept connections. - **Returns**: `Promise<{ port: number }>` - A promise that resolves with the port the server is listening on. #### `shutdown()` - **Description**: Gracefully shuts down the PlayMesh server, closing connections and releasing resources. - **Returns**: `Promise` - A promise that resolves when the shutdown is complete. #### `metrics()` - **Description**: Retrieves the current runtime metrics of the PlayMesh server. - **Returns**: `Metrics` - An object containing various performance metrics. ### Properties #### `io` - **Description**: The underlying Socket.IO server instance. Available only after the `start()` method has been called. - **Returns**: `SocketIOServer` #### `redis` - **Description**: The shared Redis client instance. Available if Redis is configured and connected. - **Returns**: `Redis` #### `queues` - **Description**: The QueueManager instance, used for managing background jobs. Requires Redis to be configured. - **Returns**: `QueueManager` ``` -------------------------------- ### Create a PlayMesh Client Source: https://playmesh.dev/docs/getting-started This TypeScript code demonstrates how to connect to a PlayMesh server, listen for chat and system events, and send chat messages. It includes client-side authentication and logs connection details. ```typescript import { PlayMeshClient } from '@playmesh/client'; const client = new PlayMeshClient({ url: 'http://localhost:4000', auth: { username: 'alice' }, }); // Listen for events from the server client.on('chat', payload => { const { sender, message } = payload as { sender: string; message: string }; console.log(`${sender}: ${message}`); }); client.on('system', payload => { console.log(`* ${(payload as { text: string }).text}`); }); // Connect and authenticate const session = await client.connect(); console.log('Connected as', session.userId); console.log('Member of:', session.instances); // Send events to the server client.emit('chat', { message: 'Hello, world!' }); ``` -------------------------------- ### Create and Broadcast in Domain Source: https://playmesh.dev/docs/architecture Create a logical grouping of instances (Domain) and broadcast messages to all sessions within that domain across all its instances. ```javascript const world = mesh.createDomain('world'); const social = mesh.createDomain('social'); // Broadcast to everyone in 'world', across all its instances world.broadcast('maintenance-warning', { minutesLeft: 5 }); ``` -------------------------------- ### Instance Methods Source: https://playmesh.dev/docs/server-sdk Methods for managing and interacting with Instances. Instances are isolated multiplayer environments where sessions can join and communicate. ```APIDOC ## Instance An isolated multiplayer environment. Sessions join to receive broadcasts and have events routed. ### Properties - **`id`** (`string`): The unique identifier for the instance. - **`domain`** (`Domain`): The Domain object that owns this instance. - **`path`** (`string`): A globally unique reference in the format "domainId/instanceId". - **`sessions`** (`Session[]`): An array of sessions currently on this node that are members of the instance. Note: This is local-node only. - **`state`** (`ScopedState`): Synchronized runtime key-value state for the instance. ### Event Handling - **`on(event, handler)`**: Registers an event handler for the specified event. - Returns: `this` - **`off(event, handler)`**: Removes an event handler for the specified event. - Returns: `this` ### Instance Communication - **`broadcast(event, payload?)`**: Sends a message to all member sessions across all nodes. - Returns: `void` ### Membership - **`memberCount()`**: Returns the total member count across all nodes for this instance. - Returns: `Promise` ### Event Hooks - **`onJoin(hook)`**: Registers a callback function to be called when a session joins the instance. - Returns: `this` - **`onLeave(hook)`**: Registers a callback function to be called when a session leaves the instance. - Returns: `this` ``` -------------------------------- ### PlayMesh Chat Server Implementation Source: https://playmesh.dev/docs/examples Sets up a PlayMesh server for a chat lobby. Handles authentication, admission of clients to instances, and real-time chat events. ```typescript import { PlayMesh } from '@playmesh/server'; const port = Number(process.env.PORT ?? 4000); const mesh = new PlayMesh({ port }); mesh.onAuthenticate(async request => { const username = String(request.auth.username ?? '').trim(); if (!username) throw new Error('A username is required'); return { userId: username }; }); mesh.bootstrap(async ({ mesh }) => { mesh.createDomain('social').createInstance('lobby'); }); mesh.onAdmission(async () => ({ instances: ['social/lobby'] })); mesh.onStarted(() => { const lobby = mesh.domain('social').instance('lobby'); lobby.onJoin(session => { lobby.broadcast('system', { text: `${session.userId} joined` }); }); lobby.onLeave(session => { lobby.broadcast('system', { text: `${session.userId} left` }); }); lobby.on('chat', (session, payload) => { lobby.broadcast('chat', { sender: session.userId, message: (payload as { message: string }).message, }); }); }); await mesh.start(); ``` -------------------------------- ### Topology Methods Source: https://playmesh.dev/docs/server-sdk Manage Domains and resolve Instances within the PlayMesh topology. ```APIDOC ## Topology Methods ### Description Methods for managing Domains (creating, retrieving, checking existence) and resolving Instances within the PlayMesh topology. ### Methods #### `createDomain(id: string)` - **Description**: Create and register a new Domain. - **Parameters**: - `id` (string): The unique identifier for the new domain. - **Returns**: `Domain` #### `domain(id: string)` - **Description**: Get an existing Domain by its ID. Throws an error if the domain is not found. - **Parameters**: - `id` (string): The ID of the domain to retrieve. - **Returns**: `Domain` #### `hasDomain(id: string)` - **Description**: Check if a Domain with the specified ID exists. - **Parameters**: - `id` (string): The ID of the domain to check. - **Returns**: `boolean` #### `resolveInstance(ref: string)` - **Description**: Resolve an Instance using its reference string (e.g., "domainId/instanceId") or a bare instance ID. - **Parameters**: - `ref` (string): The reference string or instance ID. - **Returns**: `Instance` ### Properties #### `domains` - **Description**: Access all registered Domains. - **Returns**: `Domain[]` #### `sessions` - **Description**: Access all active Sessions on the current node. - **Returns**: `Session[]` ``` -------------------------------- ### Connection Methods Source: https://playmesh.dev/docs/client-sdk Manage the client's connection to the PlayMesh server. Connect to establish a session and disconnect to close it. ```APIDOC ## Connection ### `connect()` - **Description**: Connect and authenticate. Resolves with SessionInfo once the server establishes the session. - **Returns**: `Promise` ### `disconnect()` - **Description**: Disconnect from the server. - **Returns**: `void` ### `connected` - **Description**: Whether the client is currently connected. - **Returns**: `boolean` ### Example ```typescript const session = await client.connect(); // session: { id: string, userId: string, instances: string[] } console.log('Connected as', session.userId); console.log('Instances:', session.instances); client.disconnect(); ``` ``` -------------------------------- ### Event Handling Source: https://playmesh.dev/docs/client-sdk Send events to the server using `emit` and listen for events from the server using `on` and `off`. ```APIDOC ## Events ### `emit(event, payload?)` - **Description**: Send an event to the server. Throws if not connected. - **Returns**: `void` ### `on(event, handler)` - **Description**: Listen for an event from the server. - **Returns**: `this` ### `off(event, handler)` - **Description**: Remove a listener. - **Returns**: `this` ### Example ```typescript client.on('chat', payload => { const { sender, message } = payload as { sender: string; message: string }; appendMessage(`${sender}: ${message}`); }); client.emit('move', { x: 100, y: 250 }); const handler = (payload: unknown) => { /* ... */ }; client.on('update', handler); client.off('update', handler); ``` ``` -------------------------------- ### Session Joining and Management Source: https://playmesh.dev/docs/architecture Manage a client's connection (Session) by joining it to multiple instances and checking its presence. Allows sending direct messages to the session. ```javascript await session.join(cityInstance); await session.join(guildInstance); await session.join(partyInstance); session.isIn(cityInstance); // true session.instances; // [cityInstance, guildInstance, partyInstance] await session.leave(partyInstance); session.send('inventory-update', { items }); ``` -------------------------------- ### Create a Multiplayer Chat Server Source: https://playmesh.dev/docs/getting-started This TypeScript code sets up a PlayMesh server for a chat lobby. It handles authentication, domain/instance creation, chat message broadcasting, and session join/leave events. It runs in-memory by default. ```typescript import { PlayMesh } from '@playmesh/server'; const mesh = new PlayMesh({ port: 4000 }); // 1. Authenticate connections mesh.onAuthenticate(async request => { const username = String(request.auth.username ?? '').trim(); if (!username) throw new Error('Username required'); return { userId: username }; }); // 2. Create domains and instances before connections arrive mesh.bootstrap(async ({ mesh }) => { const social = mesh.createDomain('social'); const lobby = social.createInstance('lobby'); lobby.on('chat', (session, payload) => { const { message } = payload as { message: string }; lobby.broadcast('chat', { sender: session.userId, message, }); }); lobby.onJoin(session => { lobby.broadcast('system', { text: `${session.userId} joined` }); }); lobby.onLeave(session => { lobby.broadcast('system', { text: `${session.userId} left` }); }); }); // 3. Decide which instances each new session joins mesh.onAdmission(async () => ({ instances: ['social/lobby'], })); await mesh.start(); console.log('Server running on port 4000'); ``` -------------------------------- ### Manual Session Joining with onSessionCreate Source: https://playmesh.dev/docs/auth-admission This hook allows for manual control over session joining. It's useful for complex scenarios where instances are determined dynamically based on player data or game state. ```javascript mesh.onSessionCreate(async session => { const player = await db.players.findById(session.userId); const zone = mesh.domain('world').instance(player.currentZone); await session.join(zone); if (player.guildId) { const guild = mesh.domain('guild').instance(player.guildId); await session.join(guild); } }); ``` -------------------------------- ### PlayMesh Chat Client Implementation Source: https://playmesh.dev/docs/examples Connects to a PlayMesh chat server and allows users to send and receive messages. Requires a username as a command-line argument. ```typescript import { createInterface } from 'node:readline'; import { PlayMeshClient } from '@playmesh/client'; const username = process.argv[2] ?? `guest-${Math.floor(Math.random() * 1000)}`; const client = new PlayMeshClient({ url: `http://localhost:${process.env.PORT ?? 4000}`, auth: { username }, }); client.on('chat', payload => { const { sender, message } = payload as { sender: string; message: string }; console.log(`${sender}: ${message}`); }); client.on('system', payload => { console.log(`* ${(payload as { text: string }).text}`); }); const session = await client.connect(); console.log(`Connected as ${session.userId}`); const rl = createInterface({ input: process.stdin }); rl.on('line', line => { if (line.trim()) client.emit('chat', { message: line.trim() }); }); ``` -------------------------------- ### Configure Authentication Source: https://playmesh.dev/docs/client-sdk Configure the client with static or dynamic authentication. For static tokens, provide an object with a token. For dynamic tokens, provide an async function that returns the authentication payload. ```typescript // Static token const client = new PlayMeshClient({ url: 'https://game.example.com', auth: { token: localStorage.getItem('jwt') }, }); // Dynamic — refreshed on each reconnect const client = new PlayMeshClient({ url: 'https://game.example.com', auth: async () => ({ token: await authService.getToken() }), }); ``` -------------------------------- ### Implement onAdmission Hook Source: https://playmesh.dev/docs/auth-admission Use the `onAdmission` hook to specify which instances a new session should join after authentication. Instance references can be domain/instance paths or bare instance IDs. ```javascript mesh.onAdmission(async request => { const player = await db.players.findById(request.userId); return { instances: [ 'world/city-center', `guild/${player.guildId}`, 'social/global-chat', ], }; }); ``` -------------------------------- ### Session Methods Source: https://playmesh.dev/docs/server-sdk Provides methods for managing individual client sessions, including sending messages, managing instance membership, and disconnecting clients. ```APIDOC ## Session Object Represents a single connected client. ### Properties - **id** (string) - Socket ID (unique per connection). - **userId** (string) - Assigned by your authenticate hook. - **data** (Record) - Application data from the auth result. - **instances** (Instance[]) - Instances this session is currently a member of. ### Methods - **isIn(instance)** (boolean) - Check membership by Instance or id string. - **join(instance)** (Promise) - Join an instance. No-op if already a member. - **leave(instance)** (Promise) - Leave an instance. No-op if not a member. - **send(event, payload?)** (void) - Send an event to this client only. - **disconnect()** (void) - Forcibly disconnect this client. ``` -------------------------------- ### Instance Membership Source: https://playmesh.dev/docs/client-sdk The client automatically updates its `instances` property based on server-sent `playmesh:joined` and `playmesh:left` events. Direct requests to join or leave instances are not supported. ```APIDOC ## Instance membership The client automatically maintains `client.instances` as the server sends `playmesh:joined` and `playmesh:left` events. ℹ Instance membership is controlled entirely by the server. The client cannot directly request to join or leave instances. ``` -------------------------------- ### Server Handling Client Events Source: https://playmesh.dev/docs/communication Register event handlers on server instances using `on`. Events from a session are routed to all instances the session belongs to that have a handler for that event. The handler receives the session and payload, and can broadcast events. ```javascript city.on('move', async (session, payload) => { const { x, y } = payload as { x: number; y: number }; city.broadcast('player-moved', { userId: session.userId, x, y }); }); ``` ```javascript city.on('chat', (session, payload) => { const { message } = payload as { message: string }; city.broadcast('chat', { sender: session.userId, message }); }); ``` -------------------------------- ### Metrics Source: https://playmesh.dev/docs/server-sdk Retrieves metrics about the server's current state, including active sessions, domains, instances, and uptime. ```APIDOC ## Metrics ### Description Retrieves metrics about the server's current state. ### Usage ```javascript const m = mesh.metrics(); // m will be an object with the following properties: // { // sessions: number, — active sessions on this node // domains: number, // instances: number, // uptimeMs: number, // } ``` ``` -------------------------------- ### Implement onAuthenticate Hook Source: https://playmesh.dev/docs/auth-admission Use this hook to verify incoming connections and return a user ID. The connection is rejected if the hook throws an error. Available properties on the request object include `token`, `auth`, `headers`, and `address`. ```javascript mesh.onAuthenticate(async request => { // request.token — shortcut for request.auth.token // request.auth — full auth payload from the client // request.headers — HTTP handshake headers // request.address — client IP const user = await authService.verifyToken(request.token); return { userId: user.id, // required data: { role: user.role }, // optional — available on session.data }; }); ``` -------------------------------- ### Client Receiving Events Source: https://playmesh.dev/docs/communication Listen for events from the server on the client using `client.on`. You can also remove listeners with `client.off` when they are no longer needed. ```javascript client.on('world-event', payload => { gameEngine.triggerEvent(payload); }); ``` ```javascript const onChat = (payload: unknown) => renderChat(payload); client.on('chat', onChat); ``` ```javascript // Remove when no longer needed client.off('chat', onChat); ``` -------------------------------- ### Domain Methods Source: https://playmesh.dev/docs/server-sdk Methods for managing and interacting with Domains. Domains represent isolated multiplayer environments that can contain multiple Instances. ```APIDOC ## Domain Represents an isolated multiplayer environment that can contain multiple Instances. ### Methods - **`mesh.createDomain(id)`**: Creates a new Domain with the specified ID. - **`mesh.domain(id)`**: Retrieves an existing Domain by its ID. ### Properties - **`id`** (`string`): The unique identifier for the domain. - **`instances`** (`Instance[]`): An array of all Instances within this domain. ### Instance Management - **`createInstance(id)`**: Creates a new Instance within the domain. - Returns: `Instance` - **`instance(id)`**: Retrieves a specific Instance by its ID. - Returns: `Instance` - **`hasInstance(id)`**: Checks if an Instance with the given ID exists. - Returns: `boolean` - **`destroyInstance(id)`**: Removes sessions, clears state, and deletes the instance. - Returns: `Promise` ### Domain Communication - **`broadcast(event, payload?)`**: Sends a message to every session in all instances of this domain. - Returns: `void` ### Event Hooks - **`onInstanceCreated(hook)`**: Registers a callback function to be called when a new instance is created. - Returns: `this` - **`onInstanceDestroyed(hook)`**: Registers a callback function to be called when an instance is destroyed. - Returns: `this` ``` -------------------------------- ### Client Emitting Events Source: https://playmesh.dev/docs/communication Use the `emit` method on the client to send events to the server. Include any necessary data as a payload object. ```javascript client.emit('move', { x: 100, y: 250 }); ``` ```javascript client.emit('chat', { message: 'Hello!' }); ``` ```javascript client.emit('buy-item', { itemId: 'sword-of-doom' }); ``` -------------------------------- ### Hook Management Source: https://playmesh.dev/docs/server-sdk Register callback functions for various server events like authentication, admission, and session lifecycle. ```APIDOC ## Hook Management ### Description Register callback functions (hooks) to customize server behavior at different stages, including server startup, authentication, session management, and connection events. ### Methods #### `bootstrap(hook: (context: { mesh: PlayMesh }) => Promise)` - **Description**: Register an asynchronous setup function to run before the server starts accepting connections. - **Parameters**: - `hook` (function): An async function that receives a context object containing the `mesh` instance. - **Returns**: `this` #### `onAuthenticate(hook: (request: any) => Promise)` - **Description**: Register a hook to handle session authentication. The hook should return an object containing authentication details (e.g., `userId`). - **Parameters**: - `hook` (function): An async function that receives the authentication request and returns authentication data. - **Returns**: `this` #### `onAdmission(hook: (request: any) => Promise)` - **Description**: Register a hook to determine which instances a session should join upon connection. - **Parameters**: - `hook` (function): An async function that decides instance admission. - **Returns**: `this` #### `onSessionCreate(hook: (session: any) => Promise)` - **Description**: Register a hook that is called once for each new session after it has been authenticated. - **Parameters**: - `hook` (function): An async function that executes after a session is created. - **Returns**: `this` #### `onConnect(hook: (session: any) => Promise)` - **Description**: Register a hook that is called when a session successfully connects to the server. - **Parameters**: - `hook` (function): An async function that executes upon session connection. - **Returns**: `this` #### `onDisconnect(hook: (session: any) => Promise)` - **Description**: Register a hook that is called when a session disconnects from the server. - **Parameters**: - `hook` (function): An async function that executes upon session disconnection. - **Returns**: `this` #### `onStarted(hook: () => Promise)` - **Description**: Register a hook that is called after the server has successfully started listening for connections. - **Parameters**: - `hook` (function): An async function to execute once the server is started. - **Returns**: `this` #### `onShutdown(hook: () => Promise)` - **Description**: Register a hook that is called at the beginning of the server's graceful shutdown process. - **Parameters**: - `hook` (function): An async function to execute during shutdown initiation. - **Returns**: `this` ``` -------------------------------- ### Types Source: https://playmesh.dev/docs/client-sdk Interface definitions for `SessionInfo` and `ServerError` used within the PlayMeshClient. ```APIDOC ## Types ```typescript interface SessionInfo { id: string; userId: string; instances: string[]; // "domainId/instanceId" paths } interface ServerError { scope: 'connection' | 'event'; event?: string; message: string; } ``` ``` -------------------------------- ### PlayMesh TypeScript Configuration Source: https://playmesh.dev/docs/getting-started A sample tsconfig.json file for a PlayMesh project, specifying target, module resolution, and strictness options for TypeScript compilation. ```json { "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true } } ``` -------------------------------- ### Handle Server Events Source: https://playmesh.dev/docs/client-sdk Listen for events from the server using `on` and remove listeners with `off`. Emit events to the server using `emit`. Ensure the client is connected before emitting. ```typescript client.on('chat', payload => { const { sender, message } = payload as { sender: string; message: string }; appendMessage(`${sender}: ${message}`); }); client.emit('move', { x: 100, y: 250 }); const handler = (payload: unknown) => { /* ... */ }; client.on('update', handler); client.off('update', handler); ``` -------------------------------- ### Instance Broadcast Source: https://playmesh.dev/docs/communication Broadcast messages to all sessions within a specific instance across all nodes using `instance.broadcast`. This is useful for in-game events or instance-specific updates. ```javascript city.broadcast('world-event', { event: 'dragon-spawned' }); ``` ```javascript dungeon.on('boss-killed', async (session, payload) => { const loot = await lootService.generate(payload.bossId); dungeon.broadcast('loot-dropped', { items: loot, killer: session.userId }); }); ``` -------------------------------- ### Connect and Disconnect Client Source: https://playmesh.dev/docs/client-sdk Connect to the PlayMesh server and authenticate. Disconnect the client when no longer needed. The connect method resolves with session information upon successful connection. ```typescript const session = await client.connect(); // session: { id: string, userId: string, instances: string[] } console.log('Connected as', session.userId); console.log('Instances:', session.instances); client.disconnect(); ```