Copy the .env.example file to .env and adjust the configuration to your needs.
cp .env.example .envPrerequisites: Node.js, nodemon
- Clone the repository or download the source code.
- Run
npm installin the root directory of the project. - Run
npm startto start the server. - The websocket will be available at
ws://localhost:9000.
Prerequisites: Docker
- Clone the repository or download the source code.
- Run
docker compose up --buildin the root directory of the project. - The websocket will be available at
ws://localhost:9000.
To configure the server, edit the .env file in the root directory.
The following configuration options are available:
PORT: The port that the websocket server will be served on. (default:9000)AUTH_TOKEN: An auth token used to authenticate the handshake request. (default:"")MAX_PLAYERS: Maximum number of players that can be connected to the server. (default:1000)MAX_MATCHES: Maximum number of matches that can be active on the server. (default:100)MAX_PLAYERS_PER_MATCH: Maximum number of players per match. (default:10)CHAT_MIN_LENGTH: Minimum length of chat messages. (default:1)CHAT_MAX_LENGTH: Maximum length of chat messages. (default:256)MATCH_NAME_MIN_LENGTH: Minimum length of match names. (default:1)MATCH_NAME_MAX_LENGTH: Maximum length of match names. (default:32)MATCH_PASSWORD_MIN_LENGTH: Minimum length of match passwords. (default:8)MATCH_PASSWORD_MAX_LENGTH: Maximum length of match passwords. (default:32)PLAYER_NAME_MIN_LENGTH: Minimum length of player names. (default:1)PLAYER_NAME_MAX_LENGTH: Maximum length of player names. (default:32)
You can find an .env.example file in the root directory of the project.
Events that can be sent from the client to the server.
Send a chat message to the server. The message will be broadcasted to all players in the same match or lobby as the sender. Messages will not be stored on the server.
Messages that are too short will be dropped. Messages that are too long will be truncated.
| Property | Type | Description |
|---|---|---|
message |
string required |
The message that should be sent. |
Important: This event expects a single string parameter, not an object.
Example:
// Send a chat message to the server
socket.emit('chat-message', 'Hello, World!');Requests the current status of the server. The server will respond with a status message.
Acknowledgment:
- Success:
callback(status: Status)
Example:
// Request the current status of the server
socket.emit('status', (status) => {
console.log(status);
});Create a new match on the server. The player that creates the match will automatically join it.
| Property | Type | Description |
|---|---|---|
name |
string required |
The name of the match. |
password |
string optional |
The password of the match, if it should be protected. |
isPrivate |
boolean optional |
Whether the match should be private. |
maxPlayers |
number optional |
The maximum number of players that can join the match. |
Acknowledgment:
- Success:
callback(match: Match) - Failure:
callback(error: string)
Example:
// Create a new match on the server
socket.emit('match-create', { name: 'My Match', maxPlayers: 4 }, (response) => {
if (response.startsWith('ERROR:')) {
console.error(response);
} else {
console.log('Match created:', response);
}
});Join an existing match on the server. The player will be added to the match if it exists and is not full.
| Property | Type | Description |
|---|---|---|
match |
string required |
The unique identifier of the match. |
password |
string optional |
The password of the match, if it is protected. |
Acknowledgment:
- Success:
callback(match: Match) - Failure:
callback(error: string)
Example:
// Join an existing match on the server
socket.emit('match-join', { match: '1234' }, (response) => {
if (response.startsWith('ERROR:')) {
console.error(response);
} else {
console.log('Match joined:', response);
}
});Leave the current match. The player will be removed from the match and will be added to the lobby. If the player is the owner of the match, the match will be canceled.
Example:
// Leave the current match
socket.emit('match-leave');Start the current match. The match will only start if the player is the owner of the match and the match is not already running.
Example:
// Start the current match
socket.emit('match-start');Finish the current match. The match will only finish if the player is the owner of the match and the match is running.
Example:
// Finish the current match
socket.emit('match-finish');Update the player's data. The data will be broadcasted to all players in the same match or lobby. You just need to send the properties that you want to update. The player's data will be merged with the new data.
| Property | Type | Description |
|---|---|---|
name |
string optional |
The new name of the player. |
data |
object optional |
The new additional data of the player. |
isReady |
boolean optional |
Whether the player is ready to start a match. |
Example:
// Update the player's data
socket.emit('player-update', { isReady: true });Kick a player from the current match. The player will be removed from the match and will be added to the lobby. The player will be notified that they were kicked. Only the owner of the match can kick players.
| Property | Type | Description |
|---|---|---|
player |
string required |
The unique identifier of the player that should be kicked. |
Example:
// Kick a player from the current match
socket.emit('player-kick', { player: '1234' });Sends a tick to the server. The server will proxy the tick to the owner of the match. The owner of the match will receive the tick and can respond with a tock. All validation and game logic should be handled on the client-side of the owner of the match.
Important: Ticks can only be sent by guests of the match.
| Property | Type | Description |
|---|---|---|
type |
string required |
The type of the tick. Default: tick |
data |
object required |
Context data that should be sent with the tick. |
Example:
// Send a tick to the server
socket.emit('tick', { type: 'move', data: { x: 10, y: 20 } });Send a tock to the server. The server will either broadcast the tock to all players in the same match or to a specific player. The tock can be used to respond to a tick. It can also be used to sync data between clients. All validation and game logic should be handled on the client-side of the owner of the match.
Important: Tocks can only be sent by the owner of the match.
| Property | Type | Description |
|---|---|---|
type |
string required |
The type of the tock. Default: tock |
data |
object required |
Context data that should be sent with the tock. |
Example:
// Send a tock to the server
socket.emit('tock', { type: 'sync', data: { positions: [10, 20, 30] } });Events that are sent from the server to the client.
Sends the initial data to the client. The client can use this data to initialize the game state. The initial data includes the player & the list of matches.
| Property | Type | Description |
|---|---|---|
player |
Player |
The player that is currently connected to the server. |
matches |
Match[] |
The list of matches that are currently available. |
settings |
Settings |
The settings of the server. |
Example:
// Listen for the 'init' event
socket.on('init', (data) => {
const { player } = data;
// Initialize the game state
// ...
});Sends an error message to the client.
Example:
// Listen for the 'error' event
socket.on('error', (message) => {
// Display the message to the player
// ...
});Sends a chat message to the client. Received chat messages will be displayed in the chat window of the client.
| Property | Type | Description |
|---|---|---|
player |
Player |
The player that sent the message. |
message |
string |
The message that was sent. |
Example:
// Listen for the 'chat-message' event
socket.on('chat-message', (data) => {
const { player, message } = data;
// Display the chat message in the chat window
// ...
});Sends an updated list of matches to the client. The client can use this list to display available matches to the player. The list will be updated whenever a match is created, joined, left, started, finished or canceled.
| Property | Type | Description |
|---|---|---|
matches |
Match[] |
The list of matches that are currently available. |
Example:
// Listen for the 'matches-updated' event
socket.on('matches-updated', (data) => {
const { matches } = data;
// Display the list of matches to the player
for (const match of matches) {
// ...
}
});Sends a notification to the client that the match has started. The client can use this event to start the game loop and to start sending ticks to the server.
Example:
// Listen for the 'match-started' event
socket.on('match-started', () => {
// Start the game loop
// ...
});Sends a notification to the client that the match has finished. The client can use this event to stop the game loop and to stop sending ticks to the server. The client can also use this event to return to the lobby.
| Property | Type | Description |
|---|---|---|
data |
string |
Context data that can be sent with the event. |
Example:
// Listen for the 'match-finished' event
socket.on('match-finished', (response) => {
// Stop the game loop
// ...
});Sends a notification to the client that the match has been canceled. The client can use this event to leave the match and to return to the lobby.
Example:
// Listen for the 'match-canceled' event
socket.on('match-canceled', () => {
// Leave the match
// ...
});Sends an updated match object to the client. The client can use this event to update the match object in the game state.
See: Match
Example:
// Listen for the 'match-updated' event
socket.on('match-updated', (match) => {
// Update the match object in the game state
// ...
});Sends a notification to the client that a player has joined the match. The client can use this event to update the list of players in the match.
See: Player
Example:
// Listen for the 'player-joined' event
socket.on('player-joined', (player) => {
// Update the list of players in the match
// ...
});Sends a notification to the client that a player has left the match. The client can use this event to update the list of players in the match.
See: Player
Example:
// Listen for the 'player-left' event
socket.on('player-left', (player) => {
// Update the list of players in the match
// ...
});Sends an updated player object to the client. The client can use this event to update the player object in the game state.
See: Player
Example:
// Listen for the 'player-updated' event
socket.on('player-updated', (player) => {
// Update the player object in the game state
// ...
});Sends a notification to the client that a player has been kicked from the match. The client can use this event to update the list of players in the match.
See: Player
Example:
// Listen for the 'player-kicked' event
socket.on('player-kicked', (player) => {
// Update the list of players in the match
// ...
});Sends a tick to the client. Only the owner of the match will receive the tick. The client can use this event to update the game state and to respond with a tock.
| Property | Type | Description |
|---|---|---|
type |
string |
The type of the tick. |
data |
object |
Context data that was sent with the tick. |
player |
Player |
The player that sent the tick. |
Example:
// Listen for the 'tick' event
socket.on('tick', (data) => {
const { type, data } = data;
// Update the game state
// ...
});Sends a tock to the client. Only guests of the match will receive the tock. The client can use this event to sync the local game state.
| Property | Type | Description |
|---|---|---|
type |
string |
The type of the tock. |
data |
object |
Context data that was sent with the tock. |
player |
Player |
The player that sent the tick. |
Example:
// Listen for the 'tock' event
socket.on('tock', (data) => {
const { type, data } = data;
// Sync the local game state
// ...
});| Property | Type | Description |
|---|---|---|
id |
string |
The unique identifier of the player. |
name |
string |
The name of the player. |
data |
object * |
An object that can be used to store additional data. |
isReady |
boolean * |
Whether the player is ready to start a match. |
*: Only available when in a / the same match. Not in lists.
| Property | Type | Description |
|---|---|---|
id |
string |
The unique identifier of the match. |
name |
string |
The name of the match. |
isPrivate |
boolean |
Whether the match is private. Private matches are not listed. |
isProtected |
boolean |
Whether the match is protected by a password. |
numPlayers |
number |
The number of players that are currently in the match. |
maxPlayers |
number |
The maximum number of players that can be in the match. |
data |
object * |
An object that can be used to store additional data. |
players |
Player[] * |
The players that are currently in the match. |
*: Only available when in a / the same match. Not in lists.
| Property | Type | Description |
|---|---|---|
numPlayers |
number |
The number of players that are currently connected to the server. |
maxPlayers |
number |
The maximum number of players that can be connected to the server. |
numMatches |
number |
The number of matches that are currently active on the server. |
maxMatches |
number |
The maximum number of matches that can be active on the server. |
| Property | Type | Description |
|---|---|---|
chatMinLength |
number |
The minimum length of chat messages. |
chatMaxLength |
number |
The maximum length of chat messages. |
matchNameMinLength |
number |
The minimum length of match names. |
matchNameMaxLength |
number |
The maximum length of match names. |
matchPasswordMinLength |
number |
The minimum length of match passwords. |
matchPasswordMaxLength |
number |
The maximum length of match passwords. |
playerNameMinLength |
number |
The minimum length of player names. |
playerNameMaxLength |
number |
The maximum length of player names. |
maxPlayersPerMatch |
number |
The maximum number of players per match. |
If you have any questions or need help with the package, join the Discord Server or open an issue on GitHub.
MIT License, Copyright (c) 2024 Christian Hanne