Best practices & recommendations¶
This section provides general recommendations and best practices to keep your codebase healthy and readable for your team. They are all optional, but if followed can improve code readability and cleanliness.
- Keep your room classes as small as possible, delegating game-specific functionality to other composable structures.
- Keep your synchronizeable data structures as small as possible
- Ideally, each class extending
Schema
should only have field definitions. - Do not implement heavy game logic inside
Schema
structures. Some logic is fine, specially if they're self-contained within the scope of the structure itself, mutating only its own properties.
- Ideally, each class extending
- Rooms should have as little code as possible, and forward actions to other structures
- Your game logic should be handled by other structures, such as:
- Custom external functions
- The Command Pattern.
- An Entity-Component System.
Unit Testing¶
See how to Unit Test your application. Applications created via npm init colyseus-app
already start with a small testing boilerplate you can modify to meet your needs.
Design Patterns¶
The Command Pattern¶
The command pattern has several advantages, such as:
- It decouples the classes that invoke the operation from the object that knows how to execute the operation.
- It allows you to create a sequence of commands by providing a queue system.
- Implementing extensions to add a new command is easy and can be done without changing the existing code.
- Have strict control over how and when commands are invoked.
- Improves code readability and the possibility of unit testing.
Usage¶
Installation
Initialize the dispatcher
in your room implementation:
import { Room } from "colyseus";
import { Dispatcher } from "@colyseus/command";
import { OnJoinCommand } from "./OnJoinCommand";
class MyRoom extends Room<YourState> {
dispatcher = new Dispatcher(this);
onCreate() {
this.setState(new YourState());
}
onJoin(client, options) {
this.dispatcher.dispatch(new OnJoinCommand(), {
sessionId: client.sessionId
});
}
onDispose() {
this.dispatcher.stop();
}
}
const colyseus = require("colyseus");
const command = require("@colyseus/command");
const { OnJoinCommand } = require("./OnJoinCommand");
class MyRoom extends colyseus.Room {
onCreate() {
this.dispatcher = new command.Dispatcher(this);
this.setState(new YourState());
}
onJoin(client, options) {
this.dispatcher.dispatch(new OnJoinCommand(), {
sessionId: client.sessionId
});
}
onDispose() {
this.dispatcher.stop();
}
}
How a command implementation looks like:
See more¶
- See command definitions
- See usage
- See implementation
Entity-Component System (ECS)¶
We currently do not have an official ECS (Entity-Component System), although we've seen members of the community implement their own solutions.
Very experimental
Some work has started trying to combine ECSY with @colyseus/schema.