Making Multiplayer Games with Colyseus, Node.js and TypeScript

Rate this content
Bookmark

By interacting with the Colyseus community over the past few years, some fundamental questions (not necessarily connected with the framework) seem to appear time and time again when developers start to build their own multiplayer games. This talk is going to cover some of these questions, as well as the most common scenarios and techniques you can start using today when building your own multiplayer game.

31 min
07 Apr, 2022

Video Summary and Transcription

Today's Talk covers making multiplayer games with Coliseus, Node.js, and Typescript. It explores the state of networking on the web, alternative servers, and how Coliseus works. The Talk also discusses client-side prediction, lag compensation, server limitations, scaling, and showcases cool games made with Coliseus. Additionally, it mentions Nakama and Unity integration for client-side prediction in multiplayer games. Coliseus is available on mobile and can be found on colossus.io or the GitHub repository.

Available in Español

1. Introduction to Coliseus and Networking

Short description:

Today, I'm going to talk about making multiplayer games with Coliseus, Node.js, and Typescript. We'll discuss the state of networking on the web, alternative servers, how Coliseus works, client-side techniques, and multiplayer games built with Coliseus. The state of networking on the web currently relies on WebSockets and WebRTC, with the potential future inclusion of Web Transport. While TCP is the only option for web-based games, UDP is generally preferred. However, many successful games have been made using TCP and WebSockets.

Hello, hello, everybody. I'm Enzo, I'm creator of Coliseus, and today I'm going to talk about making multiplayer games with Coliseus, Node.js and Typescript. I'm very excited to be here.

So, the topics of this talk are the state of networking on the web, alternative servers, how Coliseus works and its internal systems, some client-side techniques that you can apply even outside the scope of Coliseus, and a few multiplayer games built with Coliseus.

So, the state of networking on the web. Right now we have, for bidirectional connections, we have mostly WebSockets and WebRTC available. Web Transport, hopefully, is going to come in the future. WebSockets is TCP only and is available since 2011. WebRTC has been fully standardized only in the last year and is fairly complicated and involves a lot of other protocols and it does support reliable and unreliable connections. WebTransport, hopefully, is going to replace WebSocket in the future. Nobody knows when. And it's very exciting. It does support reliable and unreliable delivery and has experimental support already on Chrome since January this year. General advice for networking outside of the web is that TCP is not ideal for games and UDP is, it's hard to disagree of this, but unfortunately the web only has TCP as of now. Hopefully web transport is going to change that in the future and many successful games have been made in the past using TCP and WebSockets is what we got and there's a lot of successful games made on top of WebSockets.

2. Alternative Servers and Coliseus Framework

Short description:

In an alternative approach, the server validates and determines the state of the game, rather than relying on the client. Coliseus is a Node.js framework that uses WebSockets for transport. It provides matchmaking, room synchronization, and messaging. Rooms in Coliseus have lifecycle methods and can be created when clients request to join or create them. The matchmaking flow involves an HTTP request for seat reservation, querying for existing rooms, creating a room if necessary, and establishing a WebSocket connection. After the connection is established, the client receives the full room state and can start exchanging messages.

Okay, without further ado let's talk about alternative servers. So in an alternative approach you wouldn't trust the client so the client can say for example where he is or the client should never dictate information. The server should always be able to validate and tell the truth about the state of the game. So this is not very alternative, it might be fine if you're okay with this so this wouldn't be feasible on a multiplayer competitive game.

So an alternative to this is give more, give less information for the server. Let's say I'm pointing at a certain angle and moving forward so the server has the current position and he's going to the server is going to determine what's the next one and not the client. So this is alternative the responsibilities of the server is to hold the game logic, game state, validate client inputs and exchange messages with the clients and also the state. The responsibilities of the client is basically to be a visual representation of what's in the server and send inputs and actions for the server.

What Coliseus brings to the table, it is a Node.js framework. It's built only with WebSockets, so there's only WebSocket as a transport layer so far on Coliseus. It matchmakes players into rooms and has a built in room synchronization and message system. And it has the build blocks and the architecture so you can scale this to many servers and have many servers handle multiple rooms. And as you can see, rooms is a very basic block from Coliseus, and this is how a room definition looks like, and it has its life cycle methods, such as onCreate to set up a match, onJoin when some player joins the room, onLeave to clear this player from the room state and the other clients can react to this change, and onDispose when this room has been destroyed on the server. If you have a shared global state or something on the database, this is a good place to clear global things that this room has possibly created. In order for the clients to join this room, you need to expose this to the matchmaker. You see that there is no actual room being created at this point. Rooms are only created when the client requests to join or create them. On this example, it's the client requesting to join a game room and providing some information about himself, such as the name. For the matchmaking request, this is how the flow looks like. The client makes an HTTP request to ask for a seat reservation. The server is going to query for possible rooms that already exist. If it doesn't exist, it tries to create one and returns the room ID and session ID, which is the session reservation. After he got the session reservation, it tries to actually connect through web sockets. This is how the flow looks like from the server side perspective. First it tries to validate the user during on-off. This is totally custom and based on your own requirements. If that succeeds, it tries to call on join. At any point, you could throw an error here and the client would hit the catch block here. Yes, you could throw an error from the server and here it would go in the client. No error has happened during this process, the connection is established, and after the connection is established, the first thing is the client receives the full room state, so the client can already build the visual representation that that room has on the server and start exchanging messages and more state patches and more messages, and then it's regular web socket and bidirectional stuff.

3. Message Listening and State Mutation

Short description:

The onMessage API is fairly simple, allowing you to listen and send messages between client and server. The server can send messages to specific clients or broadcast messages to all clients in a room. The room state is based on mutable structures, synchronized every 15 milliseconds, and mutations should only be performed on the server. The structures used for the state are called Coliseus schema, which provide strong typing, incremental serialization, low output, and a client-side callback API. In this example, a board game is used to illustrate the creation of a state and assigning players based on session ID.

The onMessage API is fairly simple, so this is how you listen and send messages between client and server. The server can send a message to a particular client based on the client reference, and the server can also broadcast messages to every client inside this room, so there is no built-in way for that the server sends the broadcast for every client on every room. If you need to do that, you need to implement that for yourself.

The room state is based on mutable structures, so you can't really use any immutable structure as you would in front-end frameworks, and the mutations that you perform on those structures are synchronized automatically, at every 15 milliseconds by default. And those mutations are on-way, so you should only mutate state data on the server, so they are broadcasted back to the clients. You should never mutate the state in the client itself.

Right, the structures we use for the state are called Coliseus schema. It is strongly typed, it supports incremental serialization, has the lowest output we could possibly implement, and it provides a client-side callback API. I'll explain a bit about this in a few. It's very inspired by other serialization libraries such as protocol buffers, flat buffers, and others. On this example, imagine a board game. You have a map of players here, and the player has a position. A position on the map, or on the board. And on the room code, you would initially create a state, so you set state during creation, and whenever a player joins this room, we assign to the map of players a new player based on the session ID of that client.

4. Client-side Prediction and Lag Compensation

Short description:

The session ID is the unique identifier for this client on this room. On the client side, every client should listen to adds and removals from this player structure. What's the difference between messages and room state? Messages are ephemeral, while state is persistent. On multiplayer games, lag is inevitable, and developers should try to alleviate the perceived lag from the current player. Client-side prediction can be used to provide an immediate response to player actions. Linear interpolation is a simple technique that can have good results.

The session ID is the unique identifier for this client on this room. And whenever the client sends a move message, it gets the player reference and increments his position. As you can see this is not very alternative. Any player could send this at any moment and it would increment his position. So ideally, there must have some sort of validation for, like, is this player in his current turn? Does he have any pending valid moves left? Like, some checks like this.

Yeah, so on the client side, every client should listen to, like, adds and removals from this player structure. So these are the callbacks you would register on the client to prepare the visual representation. This is listening to the position attribute change. A cool thing of using TypeScript is that if you do provide—this is optional by the way—if you do provide the type reference for the room state here, you would have auto completion for all the properties and everything inside the state during development.

Yeah, so what's the difference between messages and room state? Well messages are ephemeral and they're not persistent anywhere. So whenever you send a message, only the current connected clients are going to receive it. State on the other hand is persistent. If you set something on the state and a new client joins, that new client will receive that data. Right, so on multiplayer games lag is inevitable, and we should as developers should embrace it and just try to alleviate the perceived lag from the current player. Yeah, so take this like you have the client at position 10-10, then he sends the action to move forward and the server here, he gets that he's at 11-10, but the client could have moved immediately here, and he's going to receive back this data from the server only a couple milliseconds later. This is called the round-trip time, and yeah, this is half a round-trip time. And you could use this value to perform some techniques on the client side, to try to at least give the player an immediate response. So this example from Half the Opposite implements client-side prediction, which he basically processes players' input immediately on the client side. So whenever you press the keys to move, your player moves immediately. On the server, it cues the player actions and processes the queue at every server tick. Yeah, you can have a look at the source code there. It is applicable if you want to use this approach outside of Colesios as well. There's this really nice presentation from GDC explaining Overwatch gameplay and netcode. So I highly recommend you check this. They explain the whole client-side prediction that they did there. And yeah, they also say some techniques for dropped packets. And we can't really apply any of this on WebSockets because there's no dropped packet on a reliable connection. When we do have on web transport, we could apply such techniques. So linear interpolation is very simple and can have good results. On Mesmora, this game I made, I used linear interpolation everywhere.

5. Server Limitations, Scaling, and Cool Games

Short description:

You see that this is a tile-based game and the player moves across the tiles with linear interpolation. Having a deterministic physics is important. Server limitations, like how many CCU can a server have? I could achieve that on a cheap server, a WebSocket server running a card-slash-board game that is very slow on the pace of messaging, the server could handle 3K concurrent connections. Rooms in Coliseus are stateful and games are generally stateful. There's a persistence layer that allows any node to matchmake. So how many CCUs can Koliasis handle? It depends. I would recommend avoiding having a very large room state and optimizing your game loops to use the least amount of CPU cycles as possible. Some cool games made with Coliseus: Tiny Dolby's game, the Open Source IO Shooter, Raft Force, School Break, and Kurka.io. Night's Edge by Lightfox Games.

You see that this is a tile-based game and the player moves across the tiles with linear interpolation. Locked time step is very important for a physics-based game. It's also called fixed ticker rate. Having a deterministic physics is important because then you could have many clients and the server if you need to, simulating the physics with different FPS and having the same output.

So, server limitations, like how many…I hate this question, how many CCU can a server have? I mean you can find some material on the internet that people managed to have 1 million WebSocket connections on a single server, but that's not very realistic. You could achieve that mostly with idle connections, not exchanging any messages, and that's not realistic at all. What I could achieve is that on a cheap server, a WebSocket server running a card-slash-board game that is very slow on the pace of messaging, the server could handle 3K concurrent connections. But I wouldn't recommend having a single room with those many connections. 50 to 100 would be the ideal, and if you need more connections than that, you can have more rooms across other servers to handle this load. So, yeah, then scaling comes into play. And scalability, the rooms live in memory in Coliseus, so rooms are stateful and games are generally stateful, so you really need to, you can't use stateless approaches to, for games I don't understand how people always recommend being stateless when scaling things. And there's a persistence layer that allows any node to matchmake. So to recap, this is how the seed reservation works on a single server and when you have multiple servers, the seed reservation request would come to the load balancer and it would be forwarded to any of the active servers. And then any of the active servers would return the reservation. And then, having this information, we can make the WebSocket request directly to the server that that room is living. So how many CCUs can Koliasis handle? It depends. Yeah, I don't know. It depends on your game. You have CPU and memory limits, and everybody does, no matter if you're using Koliasis or not. So for Koliasis itself, I would recommend avoiding having a very large room state, which would increase the throughput of your rooms, and optimize your game loops to use the least amount of CPU cycles as possible. So yeah, I'm running out of time here already. The additional tooling we have is this monitor for development, it can be really useful. A load test for creating small bots and tests, how far your servers can go with automated load testing.

Some cool games made with Coliseus. This was the very first well-made game with Coliseus by Tiny Dolby, it's still available here, I believe this was released in 2015. This is made from the community, it's called the Open Source IO Shooter, it can be found here on this link. Raft Force, me and Tiny Dobbins made with Default Engine, it's also a web-game available here. School Break made by Tobias, it's also available to play here. And Kurka.io is the first player shooter I've seen that is using Coliseus and it's very fun. And finally, Night's Edge by Lightfox Games.

6. Unity Game and Poll Results

Short description:

This is a Unity game, also available to download. I hope you learned something. Let's take a look at the poll results. WebSockets seem to be the top option for multiplayer games. Socket.io should have been on the list. Many people are using Coliseus. What is Nakama?

This is a Unity game, also available to download. And that's it. I hope you learned something. I'm here, waiting for your questions. Thank you so much.

So let's first take a look at the poll results for the question we asked at the beginning. Oh! None of them. We have. I was actually interested about that. If that means people haven't made multiplayer games, or if they have and they just didn't use one of these options. Yeah. Because I don't know what else you would be using. Yeah. I was expecting playing WebSockets to be the leader.

Well, that's how I interpret this. Like for people who made, I think, multiplayer games, it sounds like WebSockets are the top option. Which makes sense because, at least for me, I've only made stuff in Socket.io, which I interpret as, it's basically just a layer of framework around WebSockets. Yeah. Oh, yeah. Socket.io should have been on this list as well. Yeah, probably. That's why I was wondering if it wasn't. No, that's cool. And then there's a bunch of people using, sorry, can you pronounce it, Kolesius? Kolesius? Is that how you pronounce it? Yeah, Kolesius, yes, correct. Kolesius, yes. Thank you. Cool. There's a bunch of people using that too. And what is Nakama? I haven't heard of that one.

QnA

Nakama and Web Game Development

Short description:

And what is Nakama? They're already in Go, so not many people have heard of it. The process of making HTML5 browser games can differ from placing elements on a screen via HTML, CSS, etc. You can have a regular game without using Canvas, but it's more common to use Canvas2D or WebGL. There are plenty of options for tools and frameworks, including little.js, Pixie.js, Play Canvas, and Babylon.

And what is Nakama? I haven't heard of that one. Which one? Sorry? Nakama. Nakama, yeah, they're already in Go, so I think that's why not many people have heard of it. They are kind of similar, but they're already in Go. Cool, good to know.

Cool, I think we can jump to some of the audience questions now. So I think the first one we have is a more general question about web games. The question says, how different is the process between placing elements on a screen via HTML, CSS, et cetera, versus making one of these HTML5 browser games? And there's a second part to it. How would you describe the current ecosystem of tools and frameworks for building these types of browser games? Especially those old retro slash game-like games? Yeah, it depends a lot. I'm not sure how to answer this question, but you can have a regular game without using Canvas at all if you manage to. It can be more tricky, but it's possible. Usually you would use Canvas2D or WebGL to actually render things in real-time. What is the second portion of the question? It's just how would you describe the current ecosystems tools and frameworks. Maybe another way to phrase it, what frameworks do you use or would you recommend? There are plenty of options currently, actually. We had a talk recently about little.js. It's one tool that you could use. I think from what I saw on that talk, it's mostly for very small outputs, so you would use that. That could possibly be used for those ad games that you see. I'm not really sure. I'm just assuming it's small. But yeah, I personally like using Pixie.js. I think tomorrow we're going to have a talk from Play Canvas. Play Canvas is also really great for 3D and we had a talk about Babylon. There's so many options. It's hard to count, actually. Yeah. I also second the Play Canvas because I like it. Also, if you're used to having an editor and things where you can place things, I think that helps a lot. Play Canvas does that.

Client-side Prediction and Unity Integration

Short description:

Client-side prediction in multiplayer games can be complicated. It involves executing actions on the client side before receiving confirmation from the server. This approach allows for immediate response to player actions but can lead to challenges with player-to-player collision. Games like Brawl Stars and League of Legends do not have player-to-player collision due to its complexity. Unity users can use Coliseus with a dedicated client and there are games made with Unity that utilize Coliseus.

Play Canvas does that. Great. We have another question just generally about client-side prediction and how it works. Can you talk just a little bit about that?

Yeah, that can be complicated. Very recently, I wrote a client-side just for simple movements. For example, it depends also on how is your input. For example, if you were using your keyboard as an input, you usually would send a message at every single frame, which can sound a bit ridiculous, but that's how it works. I mean, how is one approach to this? And then it's, I can't really explain properly, I miss my words, but it's basically you trying to execute on the client side before the server really sends the message where the thing actually is on the server. So maybe one way to think about it, because I think you mentioned this a little bit in your talk where you have a player and the angle of the player and the player wants to move forward. And instead of waiting for the server to say, you're going to be in this position, you can assume that you're going to move forward. And then when you get it back, you can say that. Yeah. It makes sense. And what can be a bit tricky about this is that you, let's say you move your client forward, but you have another... That's why it's really complicated to have a player to player collision on multiplayer games. For example, you see so many multiplayer games that don't have player to player collision because it's tricky. It's complicated. Like, Brawl Stars, like most point and click like Dota. I think League of Legends also don't have it, I'm not sure. But you have your local simulation, and like, which client should this server trust? It's easy to get a misaligned. Yeah. Cool. No, that makes sense. That sounds much more complicated. That obviously comes with a trade-off of it'll be, it'll look better if you can pull that off, because then you'll have seemingly less lag.

I think we have one more question. Someone's asking about Unity. If they're using Unity, is it possible to use Colossus? Is there a plugin or a way to use it with like a Unity WebGL plug support?

Yeah, it is. There is a client available for Unity. And yeah, there's one game that was made with Unity.

Coliseus Availability and Conclusion

Short description:

Coliseus is available on mobile and can be found on colossus.io or the GitHub repository, which provides all the necessary links and information.

It's available on mobile. It's really cool. Awesome. And if they wanted to find that, would it be on like the GitHub or the web page, the Unity? Yeah, there's colossus.io. And from the GitHub repository, you can find pretty much all the links, all the information.

Awesome. Well, I think that's all the questions we have. Thank you so much, Endo. It was great having you here. Cool.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

TechLead Conference 2023TechLead Conference 2023
35 min
A Framework for Managing Technical Debt
Let’s face it: technical debt is inevitable and rewriting your code every 6 months is not an option. Refactoring is a complex topic that doesn't have a one-size-fits-all solution. Frontend applications are particularly sensitive because of frequent requirements and user flows changes. New abstractions, updated patterns and cleaning up those old functions - it all sounds great on paper, but it often fails in practice: todos accumulate, tickets end up rotting in the backlog and legacy code crops up in every corner of your codebase. So a process of continuous refactoring is the only weapon you have against tech debt.In the past three years, I’ve been exploring different strategies and processes for refactoring code. In this talk I will describe the key components of a framework for tackling refactoring and I will share some of the learnings accumulated along the way. Hopefully, this will help you in your quest of improving the code quality of your codebases.

React Summit 2023React Summit 2023
24 min
Debugging JS
As developers, we spend much of our time debugging apps - often code we didn't even write. Sadly, few developers have ever been taught how to approach debugging - it's something most of us learn through painful experience.  The good news is you _can_ learn how to debug effectively, and there's several key techniques and tools you can use for debugging JS and React apps.
JS GameDev Summit 2022JS GameDev Summit 2022
33 min
Building Fun Experiments with WebXR & Babylon.js
Top Content
During this session, we’ll see a couple of demos of what you can do using WebXR, with Babylon.js. From VR audio experiments, to casual gaming in VR on an arcade machine up to more serious usage to create new ways of collaboration using either AR or VR, you should have a pretty good understanding of what you can do today.
Check the article as well to see the full content including code samples: article. 
React Advanced Conference 2022React Advanced Conference 2022
22 min
Monolith to Micro-Frontends
Top Content
Many companies worldwide are considering adopting Micro-Frontends to improve business agility and scale, however, there are many unknowns when it comes to what the migration path looks like in practice. In this talk, I will discuss the steps required to successfully migrate a monolithic React Application into a more modular decoupled frontend architecture.
React Advanced Conference 2023React Advanced Conference 2023
22 min
Power Fixing React Performance Woes
Next.js and other wrapping React frameworks provide great power in building larger applications. But with great power comes great performance responsibility - and if you don’t pay attention, it’s easy to add multiple seconds of loading penalty on all of your pages. Eek! Let’s walk through a case study of how a few hours of performance debugging improved both load and parse times for the Centered app by several hundred percent each. We’ll learn not just why those performance problems happen, but how to diagnose and fix them. Hooray, performance! ⚡️

Workshops on related topic

JSNation 2023JSNation 2023
116 min
Make a Game With PlayCanvas in 2 Hours
Featured WorkshopFree
In this workshop, we’ll build a game using the PlayCanvas WebGL engine from start to finish. From development to publishing, we’ll cover the most crucial features such as scripting, UI creation and much more.
Table of the content:- Introduction- Intro to PlayCanvas- What we will be building- Adding a character model and animation- Making the character move with scripts- 'Fake' running- Adding obstacles- Detecting collisions- Adding a score counter- Game over and restarting- Wrap up!- Questions
Workshop levelFamiliarity with game engines and game development aspects is recommended, but not required.
React Summit Remote Edition 2021React Summit Remote Edition 2021
87 min
Building a Shopify App with React & Node
Top Content
WorkshopFree
Shopify merchants have a diverse set of needs, and developers have a unique opportunity to meet those needs building apps. Building an app can be tough work but Shopify has created a set of tools and resources to help you build out a seamless app experience as quickly as possible. Get hands on experience building an embedded Shopify app using the Shopify App CLI, Polaris and Shopify App Bridge.We’ll show you how to create an app that accesses information from a development store and can run in your local environment.
JS GameDev Summit 2022JS GameDev Summit 2022
121 min
PlayCanvas End-to-End : the quick version
Top Content
WorkshopFree
In this workshop, we’ll build a complete game using the PlayCanvas engine while learning the best practices for project management. From development to publishing, we’ll cover the most crucial features such as asset management, scripting, audio, debugging, and much more.
JSNation 2022JSNation 2022
41 min
Build a chat room with Appwrite and React
WorkshopFree
API's/Backends are difficult and we need websockets. You will be using VS Code as your editor, Parcel.js, Chakra-ui, React, React Icons, and Appwrite. By the end of this workshop, you will have the knowledge to build a real-time app using Appwrite and zero API development. Follow along and you'll have an awesome chat app to show off!
GraphQL Galaxy 2021GraphQL Galaxy 2021
164 min
Hard GraphQL Problems at Shopify
WorkshopFree
At Shopify scale, we solve some pretty hard problems. In this workshop, five different speakers will outline some of the challenges we’ve faced, and how we’ve overcome them.

Table of contents:
1 - The infamous "N+1" problem: Jonathan Baker - Let's talk about what it is, why it is a problem, and how Shopify handles it at scale across several GraphQL APIs.
2 - Contextualizing GraphQL APIs: Alex Ackerman - How and why we decided to use directives. I’ll share what directives are, which directives are available out of the box, and how to create custom directives.
3 - Faster GraphQL queries for mobile clients: Theo Ben Hassen - As your mobile app grows, so will your GraphQL queries. In this talk, I will go over diverse strategies to make your queries faster and more effective.
4 - Building tomorrow’s product today: Greg MacWilliam - How Shopify adopts future features in today’s code.
5 - Managing large APIs effectively: Rebecca Friedman - We have thousands of developers at Shopify. Let’s take a look at how we’re ensuring the quality and consistency of our GraphQL APIs with so many contributors.
JSNation 2023JSNation 2023
57 min
0 To Auth In An Hour For Your JavaScript App
WorkshopFree
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.js backend + Vanilla JS frontend) to authenticate users with One Time Passwords (email) and OAuth, including:
- User authentication – Managing user interactions, returning session / refresh JWTs- Session management and validation – Storing the session securely for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.