import { Callout, Cards, Tabs } from "nextra/components";
The Colyseus Server instance holds the server configuration options, such as transport options, presence, matchmaking driver, etc.
- Transport is the layer for bidirectional communication between server and client.
- Presence is the implementation that enables communication between rooms and/or Node.js processes.
- Driver is the storage driver used for storing and querying rooms during matchmaking.
The recommended structure to initialize a new Colyseus server is created using npm create colyseus-app@latest command.
npm create colyseus-app@latest ./my-serverYou may add your own Room Definitions, and API routes to your server:
import { defineServer, defineRoom } from "colyseus";
import { MyRoom } from "./rooms/MyRoom";
const server = defineServer({
rooms: {
my_room: defineRoom(MyRoom),
},
express: (app) => {
// Bind your express routes here:
app.get("/", (req, res) => {
res.send("It's time to kick ass and chew bubblegum!");
});
}
});The Transport Layer is responsible for the networked communication between the server and the client. Colyseus uses TCP/WebSockets for communication by default.
Each Transport has its own options you may customize.
The default transport is `WebSocketTransport`. See [Transport → WebSocket Transport (ws)](/server/transport/ws) for more details.import { defineServer } from "colyseus";
import { WebSocketTransport } from "@colyseus/ws-transport";
const server = defineServer({
transport: new WebSocketTransport({
pingInterval: 10000
}),
});The match-making driver. Rooms are cached and queried against a Driver implementation. When scaling your Colyseus servers, you may need to provide a driver option that fits your needs.
When scaling Colyseus through multiple processes / machines, you need to provide a presence server. Read more about Scalability, and the Presence API.
import { defineServer, RedisPresence } from "colyseus";
const server = defineServer({
// ...
presence: new RedisPresence()
});The selectProcessIdToCreateRoom is a callback that allows you to customize which processs the new rooms should be created at, when your deployment uses multiple Colyseus processes.
By default, the process with the least amount of rooms is selected to create a new room.
import { defineServer } from "colyseus";
const server = defineServer({
selectProcessIdToCreateRoom: async function (roomName: string, clientOptions: any) {
return (await matchMaker.stats.fetchAll())
.sort((p1, p2) => p1.roomCount > p2.roomCount ? 1 : -1)[0]
.processId;
},
});A common alternative is to use the process with least amount of connections:
import { defineServer } from "colyseus";
const server = defineServer({
selectProcessIdToCreateRoom: async function (roomName: string, clientOptions: any) {
return (await matchMaker.stats.fetchAll())
.sort((p1, p2) => p1.ccu > p2.ccu ? 1 : -1)[0]
.processId;
},
});When set to true, this process will only handle matchmaking and will not spawn rooms locally. Room creation is delegated to other game server processes via IPC.
Default is false.
import { defineServer } from "colyseus";
const server = defineServer({
isStandaloneMatchMaker: true,
});When devMode is enabled, it is capable of restoring previous Rooms and Room State when the server restarts due to a code change, when iteratively updating your room code in a local environment.
Default is false.
import { defineServer } from "colyseus";
const server = defineServer({
devMode: true,
});Wether to register shutdown routine automatically. Default is true.
If disabled, you should call gracefullyShutdown() method manually in your shutdown process.
import { defineServer } from "colyseus";
const server = defineServer({
gracefullyShutdown: false,
});Define room types for the match-maker using the rooms configuration in defineServer().
- Rooms are not created during configuration
- Rooms are created upon client request (See frontend methods)
Parameters:
room_name- The public name of the room. You'll use this name when joining the room from the frontendRoomClass- TheRoomclassdefaultOptions- (optional) Default options to provide for room creation andonAuth/onJoinmethods
const server = defineServer({
rooms: {
// Define "chat" room
chat: defineRoom(ChatRoom),
// Define "battle" room
battle: defineRoom(BattleRoom),
// Define "battle" room with custom options
battle_woods: defineRoom(BattleRoom, { map: "woods" }),
}
})Whenever a room is created by the create() or joinOrCreate() methods, only the options defined by the filterBy() method are going to be stored internally, and used to filter out rooms in further join() or joinOrCreate() calls.
Parameters
options: string[]- a list of option names
Examples
<Tabs items={['Filter by "mode" provided by the client', 'Filter by "maxClients" server config']}>
<Tabs.Tab>
Filter match-making by mode option.
```ts filename="server.ts"
const server = defineServer({
rooms: {
battle: defineRoom(BattleRoom).filterBy(['mode'])
}
})
```
Whenever the room is created, the `mode` option is going to be stored internally.
```js filename="client.js"
client.joinOrCreate("battle", { mode: "duo" }).then(room => {/* ... */});
```
You can handle the provided option in the `onCreate()` and/or `onJoin()` to implement the requested feature inside your room implementation.
```ts filename="BattleRoom.ts"
class BattleRoom extends Room {
onCreate(options) {
if (options.mode === "duo") {
// do something!
}
}
onJoin(client, options) {
if (options.mode === "duo") {
// put this player into a team!
}
}
}
```
</Tabs.Tab>
<Tabs.Tab>
Filter match-making by built-in `maxClients` value.
The `maxClients` is an internal variable stored for matchmaking, and can be used for filtering too.
```ts filename="server.ts"
const server = defineServer({
rooms: {
battle: defineRoom(BattleRoom).filterBy(['maxClients'])
}
})
```
The client can then ask to join a room capable of handling a certain number of players.
```js filename="client.js"
client.joinOrCreate("battle", { maxClients: 10 }).then(room => {/* ... */});
client.joinOrCreate("battle", { maxClients: 20 }).then(room => {/* ... */});
```
</Tabs.Tab>
You can also give a different priority for joining rooms depending on their information upon creation.
The options parameter is a key-value object containing the field name in the left, and the sorting direction in the right. Sorting direction can be one of these values: -1, "desc", "descending", 1, "asc" or "ascending".
Example
<Tabs items={['Sorting by the built-in "clients"']}>
<Tabs.Tab>
The clients is an internal variable stored for matchmaking, which contains the current number of connected clients. On the example below, the rooms with the highest amount of clients connected will have priority. Use -1, "desc" or "descending" for descending order:
```typescript
const server = defineServer({
rooms: {
battle: defineRoom(BattleRoom).sortBy({ clients: -1 })
}
})
```
To sort by the fewest amount of players, you can do the opposite. Use `1`, `"asc"` or `"ascending"` for ascending order:
```typescript
const server = defineServer({
rooms: {
battle: defineRoom(BattleRoom).sortBy({ clients: 1 })
}
})
```
</Tabs.Tab>
To allow the LobbyRoom to receive updates from a specific room type, you should define them with realtime listing enabled:
const server = defineServer({
rooms: {
battle: defineRoom(BattleRoom).enableRealtimeListing()
}
})You can listen for matchmaking events from outside the room instance scope, such as:
"create"- when a room has been created"dispose"- when a room has been disposed"join"- when a client join a room"leave"- when a client leave a room"lock"- when a room has been locked"unlock"- when a room has been unlocked
Usage:
const server = defineServer({
rooms: {
chat: defineRoom(ChatRoom)
.on("create", (room) => console.log("room created:", room.roomId))
.on("dispose", (room) => console.log("room disposed:", room.roomId))
.on("join", (room, client) => console.log(client.id, "joined", room.roomId))
.on("leave", (room, client) => console.log(client.id, "left", room.roomId))
}
})Revert a .define() call. Makes a roomName unavailable for matchmaking. This
method is usually not recommended but it may be useful in some scenarios.
server.removeRoomType("battle");Colyseus allows you to simulate latency between the server and the client. This is a convenience method for simulating clients with high latency during development.
// Make sure to never call the `simulateLatency()` method in production.
if (process.env.NODE_ENV !== "production") {
// simulate 200ms latency between server and client.
server.simulateLatency(200);
}Binds the Transport layer into the specified port.
import { defineServer } from "colyseus";
const server = defineServer({
// ...
});
server.listen(2567);Register a custom callback that is called before the Graceful Shutdown routine starts.
server.onBeforeShutdown(async () => {
// ... custom logic
});Register a custom callback that is called after the graceful shutdown is fully complete.
server.onShutdown(async () => {
// ... custom logic
});Triggers the Graceful Shutdown routine.
server.gracefullyShutdown();This method is called automatically when the process received SIGINT or SIGTERM signal.
If gracefullyShutdown: false has been provided on Server constructor, you should call this method manually.
- Room API - Implement game sessions and handle client connections
- State Synchronization - Define and sync shared game state
- HTTP Routes - Add custom REST endpoints
- Deployment - Deploy your server to production