Colyseus Multiplayer Framework
Colyseus is an authoritative multiplayer framework for Node.js, designed for real-time applications and games. The current stable version is 0.17.9, with consistent releases bringing new features and stability improvements, such as its recent first-class Vite integration. It differentiates itself by offering robust server-side state synchronization, leveraging WebSockets for communication, and providing comprehensive SDKs for a wide array of client platforms including TypeScript, React, Unity, Godot, and GameMaker. This broad support makes it a versatile choice for developers targeting multiple environments, ensuring consistent multiplayer experiences across different game engines and web frameworks. It supports various configurable transport layers and presence/driver integrations, such as Redis, for scaling and flexibility.
Common errors
-
ERR_REQUIRE_ESM: require() of ES Module ... not supported. Instead change the require of ... to a dynamic import()
cause This error occurs when attempting to use CommonJS `require()` syntax to load a Colyseus module (or its dependencies) in a Node.js environment configured for ES Modules, or if the package itself is ESM-only. Colyseus increasingly favors ESM.fixEnsure your project uses ES Module `import` statements (e.g., `import { Server } from 'colyseus';`). If you are running Node.js >= 20.x, it's recommended to set `"type": "module"` in your `package.json` for top-level ESM support. For TypeScript, configure `"module": "Node16"` or `"ES2022"` in `tsconfig.json`. -
TypeError: (0 , colyseus_vite_1.colyseus) is not a function
cause This typically indicates an incorrect import path for the Colyseus Vite plugin or a misuse of its export, often a default import when a named import from a subpath is expected.fixVerify that the Vite plugin is imported from the correct subpath using named imports: `import { colyseus } from 'colyseus/vite';`. -
Property 'query' is missing in type '{}' but required in type 'ClientHttpRequestOptions'cause This error arises from the incorrect type inference in `@colyseus/sdk` (fixed in 0.17.40) where `client.http` methods incorrectly mandated `query` or `params` even when not needed for the specific API endpoint.fixUpdate the `@colyseus/sdk` package to version 0.17.40 or newer. This version contains type definition corrections that resolve this issue. -
Error: seat reservation expired
cause This error means that the client failed to establish a connection to a reserved room within the allocated time. Common causes include the server being under heavy load, incorrect network configuration, or mixed Colyseus package versions (e.g., mixing 0.14.x and 0.15.x).fixIncrease the `seatReservationTime` in your Colyseus server configuration to allow more time for clients to connect. Verify your server's network and scaling setup. Crucially, ensure all `@colyseus/*` packages in your `package.json` are consistent in their major/minor version (e.g., all `0.17.x`).
Warnings
- breaking Colyseus now requires Node.js version 20.x or higher. Projects running on older Node.js versions must upgrade their environment to ensure compatibility and correct execution.
- gotcha Reconnection behavior in `devMode` has been updated (since v0.17.13 for transports). The server now sends `MAY_TRY_RECONNECT` close codes instead of `FAILED_TO_RECONNECT` during HMR reloads. This allows the client SDK to retry reconnection attempts more gracefully in development.
- gotcha Type inference for `client.http.*` methods in `@colyseus/sdk` was previously flawed, potentially incorrectly requiring `query` and `params` arguments on endpoints that did not declare them, especially when `strictNullChecks` was disabled in TypeScript.
- breaking A fix in `gracefullyShutdown` ordering (in `@colyseus/core` 0.17.41) ensures pending `allowReconnection()` deferreds are rejected before room states are cached. This guarantees `onLeave()` cleanup routines run prior to state caching, preventing stale player data in restored room states.
- gotcha When using TypeScript with schema decorators (`@type()`), it's crucial to set `"experimentalDecorators": true` and `"useDefineForClassFields": false` in your `tsconfig.json` for proper property accessor definition, especially when targeting ES2022 or higher.
Install
-
npm install colyseus -
yarn add colyseus -
pnpm add colyseus
Imports
- Server
const { Server } = require('colyseus');import { Server } from 'colyseus'; - Room
const { Room } = require('colyseus');import { Room } from 'colyseus'; - colyseus (Vite plugin)
import { colyseus } from 'colyseus'; // Incorrect pathimport { colyseus } from 'colyseus/vite'; - Schema
import { Schema } from 'colyseus';import { Schema, type ArraySchema, type MapSchema } from '@colyseus/schema';
Quickstart
import { defineConfig } from 'vite';
import { colyseus } from 'colyseus/vite';
import { Server, Room } from 'colyseus';
import { Schema, type } from '@colyseus/schema';
// Define your Room State using Colyseus Schema
class MyState extends Schema {
@type('string') message: string = 'Hello, Colyseus!';
}
// Define your Colyseus Room logic
class MyRoom extends Room<MyState> {
onCreate(options: any) {
this.setState(new MyState());
console.log('MyRoom created with options:', options);
this.onMessage('hello', (client, message) => {
console.log(`${client.sessionId} sent: ${message}`);
this.state.message = `${client.sessionId} says: ${message}`;
client.send('ack', `Server received: ${message}`);
});
}
onJoin(client: any, options: any) {
console.log(`${client.sessionId} joined MyRoom.`);
this.broadcast('player_joined', `${client.sessionId} has joined!`);
}
onLeave(client: any, consented: boolean) {
console.log(`${client.sessionId} left MyRoom (consented: ${consented}).`);
this.broadcast('player_left', `${client.sessionId} has left!`);
}
onDispose() {
console.log('MyRoom disposed.');
}
}
// Create a Colyseus Server instance
const gameServer = new Server();
gameServer.define('my_room', MyRoom);
export default defineConfig({
plugins: [
colyseus({
serverEntry: '/src/server/index.ts', // Adjust path to your server entry file
serveClient: true, // Serve client-side assets alongside the server
devServer: {
server: gameServer,
port: Number(process.env.COLYSEUS_PORT ?? 2567), // Default Colyseus port or env variable
},
}),
],
build: {
rollupOptions: {
external: ['@colyseus/tools'] // Example: Externalize if not bundling server tools
}
}
});