Colyseus JS Game Server and Private Rooms, or how I rewrote everything by mistake

Search for a command to run...

No comments yet. Be the first to comment.
Hello. I'm writing a Lambda "tick" function to simulate the universe for my game. It's a different architecture than my previous idea of maintaining a 24/7 server, which simulates a busy real-time game loop. I wanted to give it a try. In this post, I...

A small tool that lets you put a "marker" on a song and have a button to recall that position with a click.

Hey there! I had a blast participating in the Appwrite hackathon and I want to share my journey with you. So, buckle up and get ready for some timer magic! Team Details Okay, before we dive in, let's get to know the team (well, it's just me actually)...

Hello. I've decided to try something for the game I'm developing. The game is a long-running online solo/coop space strategy game with resource balancing and 4x elements, but that's not the subject of this post. I decided to separate game logic code ...

Hello. For this one, I ended up using Hammer.js. I did try Interact.js which is definitely newer and more updated, but that just... well... didn't work nicely. Judging by the examples on both libraries' pages you can see if the core functionality of ...

I'm sorry. I got ill and had a refactoring syndrome 😫. It was a serious case of a rewrite. My project is an online persist-universe single/co-op space game.
See, I tried writing my game server from scratch, with all the logic of managing connections a game "server room" with broadcasts and async WebSocket communication with clients. It was then when I discovered that there are many open-source game servers 🤦♂️🎮 that are available and do a lot of the network work for you.
Being utterly curious, I chose a game server project named Colyseus and decided to try rewriting my existing infrastructure logic with it, to get a feel for it. After reviewing some of the servers in this list from awesomeopensource.com, I went with Colyseus.
The Colyseus documentation is not perfect at the moment, especially around their "room" concept which was not very clear to me at first. What they call "room name" is actually some sort of a room "class" which you can instantiate instances of. Their default use case seems to be more of a game room matcher with public rooms when you request to list the rooms available with some "room name", but in my case I only want private rooms.
Setting rooms to private makes them unlisted, but I still had to find a way to find an existing game room when a client asks to join their game by a gameName (each game has a randomly generated game "name" or "id"). The code I ended up with (it's TypeScript for me now, with Colyseus being a TypeScript project):
// !! Beware of throw-away code !!
async requestRoomReservation(req: Request, res: Response): Promise<void> {
try {
const { gameName } = req.params;
const game = await getGameByName(gameName);
if (!game) {
res.statusCode = 400;
res.send({ message: `Game '${gameName}' not found!`, gameName });
return;
}
let reservation;
try {
const rooms = await matchMaker.query({ name: "spaceGame", gameName });
const { roomId } = rooms[0];
reservation = await matchMaker.joinById(roomId, { gameName });
} catch (error) {
console.log("NO ROOM! creating");
const room = await matchMaker.createRoom("spaceGame", {
gameName,
});
reservation = await matchMaker.reserveSeatFor(room, { gameName });
}
res.send({ reservation });
} catch (error) {
res.statusCode = 500;
res.send({
message:
"Unexpected server error with reserving a seat for your room, sorry",
});
}
},
Before the client joins the Colyseus server, they should create a game through an API exposed by Express (which integrates with Colyseus on some hard-coded-by-Colyseus routes 🤮*). They can also list their personal games via that API. Once they get a gameName, they ask for a Colyseus-thing called a "reservation". I query existing rooms searching for an existing room with the game name, or create one, and then sends the reservation for the Colyseus client to consume.
- Maybe I can use them under a certain sub-route
A Colyseus room auto-disposes when all clients disconnect, and that's fine by me. The actual game's logic and state is still saved in a database and some worker is consuming jobs and changing game logic. These updates will somehow be conveyed to existing rooms and broadcast to connected clients (Redis PubSub? I over-engineer again?).
I use Checkvist to jot down my thoughts (a great alternative to WorkFlowy, IMHO), as well as doing some freestyle drawing using my Lenovo Yoga C940's integrated pen. Trying to realize how to connect the workers executing jobs, broadcasting them to clients and other things that make the game logic work, I caught myself up over-engineering things for the time of this project and decided to have all the logic in a single Heroku web worker. KISS as much as possible and get fast to a playable (pre-)MVP as a fellow developer suggested.
For scheduling tasks, such as my precious colony ships production, I intend to use Bree, but that deserves a different post. I will also write about TypeORM and how I use it with Heroku and with myself hopefully soon, but for now, I'll conclude this post and start getting my hands dirty with building colony ships.