SlideShare a Scribd company logo
Making
Online Multiplayer Game
Experiences
With Colyseus and Node.js
This presentation is from Jan 2020.
It was supposed to happen on Node Atlanta 2020,
but unfortunately it got cancelled.
Hi, I’m Endel 👋
👨💻 Software Engineer and Game Developer from Brazil
🎓 Game Development & Digital Entertainment
GitHub / Twitter: @endel
I DON’T LIKE SOCCER
Summary
● Introduction
● Basic concepts
○ How Online Multiplayer Games Work
○ Authoritative Game Servers
● Colyseus in-depth
○ Match-making
○ Game State & Serialization
○ Infrastructure
● Client-side Techniques
A naive beginning (2015)
● Socket.io
● Not authoritative
● Synchronization issues between clients
Server
Broadcasts messages back
to clients
Client 1.
State
Client 2.
State
Client 3.
State
100ms 100ms 150ms
A naive beginning (2015)
Not authoritative
A naive beginning (2015)
All I wanted
● Be able to handle multiple game sessions
● Manipulate data structures in the server
○ Have them automatically synchronized with the clients
Colyseus
Why Colyseus?
● Multiplayer games should be easier to make
● Should be more accessible
● Should work on as many platforms as possible
● Should be open-source!
Client-side Integration
● HTML5 (JavaScript / TypeScript)
● Unity3D (C#)
● Defold Engine (LUA)
● Haxe
● Cocos2d-X (C++)
How Online Multiplayer Games Work?
● Peer-to-peer
● Client as a host
● Hybrid (of two above)
● Client / Server
Server
Dumb client Dumb client Dumb client
Responsibilities of the Server
● Hold the Game State
● Hold the Game Logic
● Validate client inputs
● Send the Game State to the clients
Server
Dumb client Dumb client Dumb client
Responsibilities of the Client
● Visual representation
● Apply game state updates in a pleasant manner
● Send inputs (actions) to the Server
Server
Dumb client Dumb client Dumb client
Authoritative Game Servers
My new position is
now [x, y]
*mxlvl.com: This game was made by @x100
Authoritative Game Servers
My new position is
now [x, y]
*mxlvl.com: This game was made by @x100
Authoritative Game Servers
I’m pointing at angle
X and moving
forward
*mxlvl.com: This game was made by @x100
Authoritative Game Servers
I’m pointing at angle
X and moving
forward
*mxlvl.com: This game was made by @x100
Colyseus
● Uses HTTP/WebSockets
● Game Rooms
● Match-making
● Room State Serialization
● Scalability*
Colyseus
Game Rooms
Colyseus
Game Rooms
The match-maker has
instantiated this room.
Rooms are in-memory, and
live on a particular Node.js
process
Colyseus
Game Rooms
Client is asking to join
into this room
Colyseus
Game Rooms
Client sent a message to
this room
Colyseus
Game Rooms
Client has been
disconnected
(or left intentionally)
Colyseus
Game Rooms
The room is being
destroyed.
(It’s a good place to persist
things on the database)
Rooms are disposed
automatically when the last
client disconnects
(unless autoDispose=false)
Colyseus
Game Rooms
Colyseus
Game Rooms
Unique identifier for
this room type
Colyseus
Game Rooms
● Rooms are created during matchmaking
● A client can be connected to multiple rooms
● Each room connection has its own WebSocket connection
● client.join( “game”, { options } )
● client.create( “game”, { options } )
● client.joinOrCreate( “game”, { options } )
● client.joinById( roomId, { options } )
● client.reconnect( roomId, sessionId )
Colyseus
Matchmaking (Client-side)
Colyseus
Matchmaking (Client-side)
try {
const room = await client.joinOrCreate("game", {
name: "Jake Badlands"
});
} catch (e) {
console.log("Could not join!");
console.error(e);
}
Colyseus
Matchmaking
ient.joinOrCreate("game", {
s"
t join!");
class MyRoom extends Room {
maxClients = 4;
async onAuth(client, options) {
// retrieve user data from auth provider
return true;
}
onJoin(client, options) {
// mutate the this.state
console.log(options.name, "joined!");
}
}
Client-side: Server-side:
http: request seat reservation
roomId / sessionId
WebSocket request
Connection established
Colyseus
Matchmaking
Full Room’s State
... State patches
Connection established
Colyseus
Room State & Serialization
... State patches
● The Room State is MUTABLE
● Patches are broadcasted at every 50ms
○ Customizable via this.setPatchRate(ms)
( But first, a bit of a background… )
Colyseus
Room State & Serialization
👎 Serialization: back in v0.1 ~ v0.3 👎
● Deep JavaScript Object Comparison
● JSON Patch (RFC6902)
● Patch size is too large
[
{ "op": "remove", "path": "/players/N150OHMve" },
{ "op": "add", "path": "/players/NkfeqSGPx", "value": {"x": 10, "y": 10} },
{ "op": "replace", "path": "/players/NkfeqSGPx/x", "value": 5 }
]
Previous State ⇔ Current State
👎 Serialization: back in v0.4 ~ v0.9 👎
● “Fossil Delta” Binary Diff Compression
● Hard to detect a change in the state
● Creates a new copy when decoding
var currentState = this.state;
var currentStateEncoded = msgpack.encode( currentState );
// skip if state has not changed.
if ( currentStateEncoded.equals( this._previousStateEncoded ) ) {
return false;
}
var patches = delta.create(this._previousStateEncoded, currentStateEncoded);
CPU Intensive
Serialization: v0.10+
● @colyseus/schema
● Schema-based
● Strongly-typed
● Binary serialization
● Inspired by protobuf / flatbuffers / avro
Serialization: v0.10+
● @colyseus/schema
● Schema-based
● Strongly-typed
● Binary serialization
● Inspired by protobuf / flatbuffers / avro
🔲 Encode/Decode only fields that have changed
🔲 No bottleneck to detect state changes
🔲 Mutations should be cheap
🔲 Avoid decoding large objects that haven't been patched
🔲 Better developer experience on statically-typed languages (C#, C++)
Serialization: v0.10+
Checklist
✅ Encode/Decode only fields that have changed
✅ No bottleneck to detect state changes
❓ Mutations should be cheap (need more benchmarks, so far seems ✅)
✅ Avoid decoding large objects that haven't been patched
✅ Better developer experience on statically-typed languages (C#, C++)
Serialization: v0.10+
Checklist
class Player extends Schema {
@type("number") x: number;
@type("number") y: number;
}
const player = new Player();
player.x = 10;
player.y = 20;
player.encode()
[ 0, 10, 1, 20 ]
Definition Usage
Serialization: v0.10+
Demo
class Player extends Schema {
@type("number") x: number;
@type("number") y: number;
}
const player = new Player();
player.x = 10;
player.y = 20;
player.encode()
[ 0, 10, 1, 20 ]
Definition Usage
Serialization: v0.10+
Demo
class Player extends Schema {
@type("number") x: number;
@type("number") y: number;
}
const player = new Player();
player.x = 10;
player.y = 20;
player.encode()
[ 0, 10, 1, 20 ]
Definition Usage
Serialization: v0.10+
Demo
@colyseus/schema
Colyseus
Room State & Serialization
import { Schema, type, MapSchema } from "@colyseus/schema";
class Player extends Schema {
@type("number")
position: number = 0;
}
class GameState extends Schema {
@type({ map: Player })
players = new MapSchema<Player>();
}
Game State Example
class MyRoom extends Room {
onCreate(options) {
this.setState(new GameState());
}
onJoin(client, options) {
this.state.players[client.sessionId] = new Player();
}
onMessage(client, message) {
if (message.move) {
this.state.players[client.sessionId].position++;
}
Colyseus
Room State & Serialization
Room State Example
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.onStateChange((state) => {
console.log("You are:", state.players[ room.sessionId ])
});
Client-side: Receiving Patches
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.onStateChange((state) => {
console.log("You are:", state.players[ room.sessionId ])
});
Client-side: Receiving Patches
What exactly has changed?
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.state.players.onAdd = (player, sessionId) => {
console.log("player joined", sessionId);
player.listen("position", (position) => {
console.log("position changed", position);
});
}
room.state.players.onRemove = (player, sessionId) {
console.log("player left", sessionId);
}
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.state.players.onAdd = (player, sessionId) => {
console.log("player joined", sessionId);
player.listen("position", (position) => {
console.log("position changed", position);
});
}
room.state.players.onRemove = (player, sessionId) {
console.log("player left", sessionId);
}
Player joined
Player left
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.state.players.onAdd = (player, sessionId) => {
console.log("player joined", sessionId);
player.listen("position", (position) => {
console.log("position changed", position);
});
}
room.state.players.onRemove = (player, sessionId) {
console.log("player left", sessionId);
}
Particular
property
changes
Colyseus
Room State & Serialization
const room = await client.joinOrCreate<GameState>("game");
room.state.players.onAdd = (player, sessionId) => {
console.log("player joined", sessionId);
player.listen("position", (position) => {
console.log("position changed", position);
});
}
room.state.players.onRemove = (player, sessionId) {
console.log("player left", sessionId);
}
Autocompletion
💖 TypeScript 💖
Colyseus
Room State & Serialization
const room = await client.joinOrCreate("game");
room.state.players.onAdd = (player, sessionId) => {
console.log("player joined", sessionId);
player.listen("position", (position) => {
console.log("position changed", position);
});
}
room.state.players.onRemove = (player, sessionId) {
console.log("player left", sessionId);
}
(Consistency?!)
● By default, the entire state is sent to everyone
○ This way no “private” data can be stored in the state
● @filter() let you define a filter callback per property, per client
Colyseus
State Filters! (Experimental)
Colyseus
State Filters! (Experimental)
class Entity extends Schema {
@filter(function(client, value, root) {
// this = instance of Entity
// client = the client which this value is being filtered for
// value = `secret` value
// root = the Root State
return false;
})
@type("number") secret: number;
}
Colyseus
State Filters! (Experimental)
class Entity extends Schema {
@filter(function(client, value, root) {
// this = instance of Entity
// client = the client which this value is being filtered for
// value = `secret` value
// root = the Root State
return false;
})
@type("number") secret: number;
}
Colyseus
State Filters! (Experimental)
class Entity extends Schema {
@filter(function(client, value, root) {
// this = instance of Entity
// client = the client which this value is being filtered for
// value = `secret` value
// root = the Root State
return false;
})
@type("number") secret: number;
}
Colyseus
State Filters! (Experimental)
class Card extends Schema {
@filter(function(this: Card, client, value, root?: RootState) {
return root.
players[client.sessionId].
cards.indexOf(this) !== -1;
})
@type("number") number: number;
}
Card Game Example:
Only Card owners can see their own Card data
Colyseus
State Filters! (Experimental)
class Player extends Schema {
sessionId: string;
@filter(function(this: Player, client: any, value: Card) {
return this.sessionId === client.sessionId;
})
@type([Card]) cards = new ArraySchema<Card>();
}
⚠️ Array and Map filters are not currently supported ⚠️
(Planned for version 1.0)
Colyseus
State Filters! (Experimental)
class State extends Schema {
@filter(function(this: State, client: any, value: Player) {
const player = this.players[client.sessionId]
const a = value.x - player.x;
const b = value.y - player.y;
return (Math.sqrt(a * a + b * b)) <= 10;
})
@type({ map: Player }) players = new MapSchema<Player>();
}
⚠️ Array and Map filters are not currently supported ⚠️
(Planned for version 1.0)
using Colyseus.Schema;
public class State : Schema {
[Type("string")]
public string fieldString = "";
[Type("number")]
public float fieldNumber = 0;
[Type("ref", typeof(Player))]
public Player player = new Player(
[Type("array", typeof(ArraySchema<
public ArraySchema<Player> arrayOf
ArraySchema<Player>();
[Type("map", typeof(MapSchema<Play
Client-side integration (C#)
● npx schema-codegen 
State.ts 
--csharp 
--output client-state
generated: State.cs
Colyseus
Room State & Serialization
using namespace colyseus::schema;
class State : public Schema {
public:
string fieldString = "";
varint_t fieldNumber = 0;
Player *player = new Player();
ArraySchema<Player*> *arrayOfPlaye
ArraySchema<Player*>();
MapSchema<Player*> *mapOfPlayers =
State() {
this->_indexes = {{0, "fiel
this->_types = {{0, "strin
this->_childPrimitiveTypes
Client-side integration (C++)
● npx schema-codegen 
State.ts 
--cpp 
--output client-state
generated: State.hpp
Colyseus
Room State & Serialization
import io.colyseus.serializer.schema.Schema;
class State extends Schema {
@:type("string")
public var fieldString: String = ""
@:type("number")
public var fieldNumber: Dynamic = 0
@:type("ref", Player)
public var player: Player = new Pla
@:type("array", Player)
public var arrayOfPlayers: ArraySch
Client-side integration (Haxe)
● npx schema-codegen 
State.ts 
--haxe 
--output client-state
generated: State.hx
Colyseus
Room State & Serialization
Colyseus
Sending Messages
● Messages use MsgPack by default
room.onMessage((message) => {
console.log("received:", message)
})
room.send({ action: "hello" })
Client-side
onMessage(client, message) {
if (message.action === "hello") {
this.broadcast("world!");
this.send(client, "world!");
}
}
Server-side
Colyseus
Sending Messages
● Messages use MsgPack by default
room.onMessage((message) => {
console.log("received:", message)
})
room.send({ action: "hello" })
Client-side
onMessage(client, message) {
if (message.action === "hello") {
this.broadcast("world!");
this.send(client, "world!");
}
}
Server-side
Send message to everyone
Colyseus
Sending Messages
● Messages use MsgPack by default
room.onMessage((message) => {
console.log("received:", message)
})
room.send({ action: "hello" })
Client-side
onMessage(client, message) {
if (message.action === "hello") {
this.broadcast("world!");
this.send(client, "world!");
}
}
Server-side
Send message to a single
client
Colyseus
Handling reconnection
● What if one of my clients drops the connection?
○ Closed/Refreshed Browser
○ Switched from Wifi / 3G
○ Unstable Internet Connection
○ Etc.
class MyRoom extends Room {
async onLeave (client, consented: boolean) {
try {
if (consented) {
throw new Error("consented leave");
}
await this.allowReconnection(client, 20);
console.log("Client successfully reconnected!");
} catch (e) {
console.log("Could not reconnect.");
}
}
Colyseus
Handling reconnection (Server-side)
class MyRoom extends Room {
async onLeave (client, consented: boolean) {
try {
if (consented) {
throw new Error("consented leave");
}
await this.allowReconnection(client, 20);
console.log("Client successfully reconnected!");
} catch (e) {
console.log("Could not reconnect.");
}
}
Colyseus
Handling reconnection (Server-side)
room.leave() was
called from the
client
class MyRoom extends Room {
async onLeave (client, consented: boolean) {
try {
if (consented) {
throw new Error("consented leave");
}
await this.allowReconnection(client, 20);
console.log("Client successfully reconnected!");
} catch (e) {
console.log("Could not reconnect.");
}
}
Colyseus
Handling reconnection (Server-side)
Hold client’s seat
reservation
(sessionId)
for 20 seconds
class MyRoom extends Room {
async onLeave (client, consented: boolean) {
try {
if (consented) {
throw new Error("consented leave");
}
await this.allowReconnection(client, 20);
console.log("Client successfully reconnected!");
} catch (e) {
console.log("Could not reconnect.");
}
}
Colyseus
Handling reconnection (Server-side)
No reconnection
in 20 seconds,
promise rejected
class MyRoom extends Room {
async onLeave (client, consented: boolean) {
try {
if (consented) {
throw new Error("consented leave");
}
await this.allowReconnection(client, 20);
console.log("Client successfully reconnected!");
} catch (e) {
console.log("Could not reconnect.");
}
}
Colyseus
Handling reconnection (Server-side)
Reconnected!
const room = await client.joinOrCreate("game", {});
// Cache roomId and sessionId
localStorage.setItem("lastRoomId", room.id);
localStorage.setItem("lastSessionId", room.sessionId);
// (CLOSE BROWSER TAB)
const lastRoomId = localStorage.getItem("lastRoomId");
const lastSessionId = localStorage.getItem("lastSessionId");
const room = await client.reconnect(lastRoomId, lastSessionId);
Colyseus
Handling reconnection (Client-side)
const room = await client.joinOrCreate("game", {});
// Cache roomId and sessionId
localStorage.setItem("lastRoomId", room.id);
localStorage.setItem("lastSessionId", room.sessionId);
// (CLOSE BROWSER TAB)
const lastRoomId = localStorage.getItem("lastRoomId");
const lastSessionId = localStorage.getItem("lastSessionId");
const room = await client.reconnect(lastRoomId, lastSessionId);
Colyseus
Handling reconnection (Client-side)
Client-side Techniques
Client Server
p = [10, 10] p = [10, 10]
p = [11, 10]
p = [11, 10]
Delay
(~100ms)
Client-side Techniques
Client Server
p = [10, 10] p = [10, 10]
p = [11, 10]
p = [11, 10]
Delay
(~100ms)
Client-side Techniques
● Linear Interpolation
(Lerp)
https://mazmorra.io
tick() {
sprite.x = lerp(sprite.x, serverX, 0.07);
sprite.y = lerp(sprite.y, serverY, 0.07);
}
Client-side Techniques
● Delay to process actions
● Simulation on other client
happens 1 second later
https://poki.com/en/g/raft-wars-multiplayer
Client-side Techniques
● Client-side prediction
● Process player’s input immediately
● Enqueue player’s actions
in the server
● Process the queue at every
server tick
https://github.com/halftheopposite/tosios
Infrastructure
● How many CCU can Colyseus handle?
○ It depends!
● You have CPU and Memory limits
○ Optimize Your Game Loop
● 1000 clients in a single room?
○ Probably no!
● 1000 clients distributed across many rooms?
○ Probably yes!
Scalability ?
Scalability !
Scalability
● Colyseus’ rooms are STATEFUL
● Games are (generally) STATEFUL
● Rooms are located on a specific server and/or process.
Scalability
Colyseus
Communication between Game Rooms
onCreate() {
/**
* Pub/Sub
*/
this.presence.subscribe("global-action", (data) => {
console.log(data);
});
this.presence.publish("global-action", "hello!");
/**
* "Remote Procedure Call"
*/
matchMaker.remoteRoomCall(roomId, "methodName", [...])
}
Colyseus
Tools
@colyseus/monitor
Colyseus
Tools
@colyseus/loadtest
$ npx colyseus-loadtest bot.ts --room my_room --numClients 10
● Realtime
● Phaser/JavaScript
● Built by @tinydobbings
● ~350 concurrent players
Built with Colyseus
GunFight.io
https://gunfight.io
● Turn-based
● JavaScript version
● Defold Engine version (@selimanac)
Built with Colyseus
Tic-Tac-Toe
https://github.com/endel/colyseus-tic-tac-toe
● “The Open-Source IO Shooter”
● Realtime shooter
● PixiJS / TypeScript
● Built by @halftheopposite
Built with Colyseus
TOSIOS
https://github.com/halftheopposite/tosios
● Turn-based
● Defold Engine / LUA
● ~250 concurrent players
● (soon on mobile!)
Built with Colyseus
Raft Wars Multiplayer
https://poki.com/en/g/raft-wars-multiplayer
● Instant Game (Messenger)
● Realtime
Built with Colyseus
Chaos Shot
https://www.facebook.com/ChaosShot.io/
Plans for v1.0
● Better State Filters (on Arrays and Maps)
● Allow more sophisticated matchmaking
● Transport-independent (TCP/UDP)
● ...while keeping all client-side integrations up-to-date!
Thank You! ❤
● Twitter/GitHub: @endel

More Related Content

What's hot

OpenStack Neutron behind the Scenes
OpenStack Neutron behind the ScenesOpenStack Neutron behind the Scenes
OpenStack Neutron behind the Scenes
Anil Bidari ( CEO , Cloud Enabled)
 
Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019
Unity Technologies
 
2021 二月份 Veeam 與解決方案概觀
2021 二月份 Veeam 與解決方案概觀 2021 二月份 Veeam 與解決方案概觀
2021 二月份 Veeam 與解決方案概觀
Wales Chen
 
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
Alessandro Arrichiello
 
Instalacion Weblogic Server 12c Windows 10.
Instalacion Weblogic Server 12c Windows 10.Instalacion Weblogic Server 12c Windows 10.
Instalacion Weblogic Server 12c Windows 10.
Moisés Elías Araya
 
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
doboncho
 
OpenStack Quantum Intro (OS Meetup 3-26-12)
OpenStack Quantum Intro (OS Meetup 3-26-12)OpenStack Quantum Intro (OS Meetup 3-26-12)
OpenStack Quantum Intro (OS Meetup 3-26-12)
Dan Wendlandt
 
Accelerating HPC Applications on NVIDIA GPUs with OpenACC
Accelerating HPC Applications on NVIDIA GPUs with OpenACCAccelerating HPC Applications on NVIDIA GPUs with OpenACC
Accelerating HPC Applications on NVIDIA GPUs with OpenACC
inside-BigData.com
 
WebSphere Application Server V8.5.5 Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
WebSphere Application Server V8.5.5Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51WebSphere Application Server V8.5.5Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
WebSphere Application Server V8.5.5 Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
Takakiyo Tanaka
 
C#でOpenCL with OpenTK + Cloo
C#でOpenCL with OpenTK + ClooC#でOpenCL with OpenTK + Cloo
C#でOpenCL with OpenTK + Cloo
aokomoriuta
 
MP BGP-EVPN 실전기술-1편(개념잡기)
MP BGP-EVPN 실전기술-1편(개념잡기)MP BGP-EVPN 실전기술-1편(개념잡기)
MP BGP-EVPN 실전기술-1편(개념잡기)
JuHwan Lee
 
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
ShapeBlue
 
KVM tools and enterprise usage
KVM tools and enterprise usageKVM tools and enterprise usage
KVM tools and enterprise usage
vincentvdk
 
What Can Compilers Do for Us?
What Can Compilers Do for Us?What Can Compilers Do for Us?
What Can Compilers Do for Us?
National Cheng Kung University
 
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
裝機安 Angelo
 
EMC Part Numbers
EMC Part NumbersEMC Part Numbers
EMC Part Numbers
webmaster-ibremarketing
 
GDC 2016: Modular Level Design of Fallout 4
GDC 2016: Modular Level Design of Fallout 4 GDC 2016: Modular Level Design of Fallout 4
GDC 2016: Modular Level Design of Fallout 4
Joel Burgess
 
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
Hwanhee Kim
 
MQTT簡介與使用開放原始碼
MQTT簡介與使用開放原始碼MQTT簡介與使用開放原始碼
MQTT簡介與使用開放原始碼
Wei-Tsung Su
 
Real-time Fluid Simulation in Shadow of the Tomb Raider
Real-time Fluid Simulation in Shadow of the Tomb RaiderReal-time Fluid Simulation in Shadow of the Tomb Raider
Real-time Fluid Simulation in Shadow of the Tomb Raider
Eidos-Montréal
 

What's hot (20)

OpenStack Neutron behind the Scenes
OpenStack Neutron behind the ScenesOpenStack Neutron behind the Scenes
OpenStack Neutron behind the Scenes
 
Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019Custom SRP and graphics workflows - Unite Copenhagen 2019
Custom SRP and graphics workflows - Unite Copenhagen 2019
 
2021 二月份 Veeam 與解決方案概觀
2021 二月份 Veeam 與解決方案概觀 2021 二月份 Veeam 與解決方案概觀
2021 二月份 Veeam 與解決方案概觀
 
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
[Devconf.cz][2017] Understanding OpenShift Security Context Constraints
 
Instalacion Weblogic Server 12c Windows 10.
Instalacion Weblogic Server 12c Windows 10.Instalacion Weblogic Server 12c Windows 10.
Instalacion Weblogic Server 12c Windows 10.
 
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
「HDR広色域映像のための色再現性を考慮した色域トーンマッピング」スライド Color Gamut Tone Mapping Considering Ac...
 
OpenStack Quantum Intro (OS Meetup 3-26-12)
OpenStack Quantum Intro (OS Meetup 3-26-12)OpenStack Quantum Intro (OS Meetup 3-26-12)
OpenStack Quantum Intro (OS Meetup 3-26-12)
 
Accelerating HPC Applications on NVIDIA GPUs with OpenACC
Accelerating HPC Applications on NVIDIA GPUs with OpenACCAccelerating HPC Applications on NVIDIA GPUs with OpenACC
Accelerating HPC Applications on NVIDIA GPUs with OpenACC
 
WebSphere Application Server V8.5.5 Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
WebSphere Application Server V8.5.5Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51WebSphere Application Server V8.5.5Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
WebSphere Application Server V8.5.5 Libertyプロファイルのご紹介 #jjug_ccc #ccc_r51
 
C#でOpenCL with OpenTK + Cloo
C#でOpenCL with OpenTK + ClooC#でOpenCL with OpenTK + Cloo
C#でOpenCL with OpenTK + Cloo
 
MP BGP-EVPN 실전기술-1편(개념잡기)
MP BGP-EVPN 실전기술-1편(개념잡기)MP BGP-EVPN 실전기술-1편(개념잡기)
MP BGP-EVPN 실전기술-1편(개념잡기)
 
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
Deploying CloudStack and Ceph with flexible VXLAN and BGP networking
 
KVM tools and enterprise usage
KVM tools and enterprise usageKVM tools and enterprise usage
KVM tools and enterprise usage
 
What Can Compilers Do for Us?
What Can Compilers Do for Us?What Can Compilers Do for Us?
What Can Compilers Do for Us?
 
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
HPE SimpliVity install mgmt guide 201907-01 (Taiwan-Chinese) ;HPE SimpliVity ...
 
EMC Part Numbers
EMC Part NumbersEMC Part Numbers
EMC Part Numbers
 
GDC 2016: Modular Level Design of Fallout 4
GDC 2016: Modular Level Design of Fallout 4 GDC 2016: Modular Level Design of Fallout 4
GDC 2016: Modular Level Design of Fallout 4
 
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
[NDC2016] 신경망은컨텐츠자동생성의꿈을꾸는가
 
MQTT簡介與使用開放原始碼
MQTT簡介與使用開放原始碼MQTT簡介與使用開放原始碼
MQTT簡介與使用開放原始碼
 
Real-time Fluid Simulation in Shadow of the Tomb Raider
Real-time Fluid Simulation in Shadow of the Tomb RaiderReal-time Fluid Simulation in Shadow of the Tomb Raider
Real-time Fluid Simulation in Shadow of the Tomb Raider
 

Similar to NodeAtlanta 2020 - Making Multiplayer Games with Colyseus and Node.js.pptx

Scaling a Game Server: From 500 to 100,000 Users
Scaling a Game Server: From 500 to 100,000 UsersScaling a Game Server: From 500 to 100,000 Users
Scaling a Game Server: From 500 to 100,000 Users
GeekNightHyderabad
 
Scalability & Big Data challenges in real time multiplayer games
Scalability & Big Data challenges in real time multiplayer gamesScalability & Big Data challenges in real time multiplayer games
Scalability & Big Data challenges in real time multiplayer games
Yan Cui
 
Let s Enjoy Node.js
Let s Enjoy Node.jsLet s Enjoy Node.js
Let s Enjoy Node.js
Fred Chien
 
Node lt
Node ltNode lt
Node lt
snodar
 
Defcon CTF quals
Defcon CTF qualsDefcon CTF quals
Defcon CTF quals
snyff
 
NetRacer for the Commodore 64
NetRacer for the Commodore 64NetRacer for the Commodore 64
NetRacer for the Commodore 64
Leif Bloomquist
 
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
tdc-globalcode
 
Akka for realtime multiplayer mobile games
Akka for realtime multiplayer mobile gamesAkka for realtime multiplayer mobile games
Akka for realtime multiplayer mobile games
Yan Cui
 
Mobile webapplication development
Mobile webapplication developmentMobile webapplication development
Mobile webapplication development
Ganesh Gembali
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsMario Gonzalez
 
Game Programming - Cloud Development
Game Programming - Cloud DevelopmentGame Programming - Cloud Development
Game Programming - Cloud Development
Nick Pruehs
 
Multiplayer Java Game
Multiplayer Java GameMultiplayer Java Game
Multiplayer Java Game
karim baidar
 
Building fast,scalable game server in node.js
Building fast,scalable game server in node.jsBuilding fast,scalable game server in node.js
Building fast,scalable game server in node.js
Xie ChengChao
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_js
Mario Gonzalez
 
Clojure ♥ cassandra
Clojure ♥ cassandra Clojure ♥ cassandra
Clojure ♥ cassandra
Max Penet
 
On Space-Scarce Economy In Blockchain Systems
On Space-Scarce Economy In Blockchain SystemsOn Space-Scarce Economy In Blockchain Systems
On Space-Scarce Economy In Blockchain Systems
Dmitry Meshkov
 
Game server development in node.js in jsconf eu
Game server development in node.js in jsconf euGame server development in node.js in jsconf eu
Game server development in node.js in jsconf eu
Xie ChengChao
 
Building Multiplayer Games (w/ Unity)
Building Multiplayer Games (w/ Unity)Building Multiplayer Games (w/ Unity)
Building Multiplayer Games (w/ Unity)
Noam Gat
 
Monte Carlo C++
Monte Carlo C++Monte Carlo C++
Monte Carlo C++
Dmitri Nesteruk
 

Similar to NodeAtlanta 2020 - Making Multiplayer Games with Colyseus and Node.js.pptx (20)

Scaling a Game Server: From 500 to 100,000 Users
Scaling a Game Server: From 500 to 100,000 UsersScaling a Game Server: From 500 to 100,000 Users
Scaling a Game Server: From 500 to 100,000 Users
 
Scalability & Big Data challenges in real time multiplayer games
Scalability & Big Data challenges in real time multiplayer gamesScalability & Big Data challenges in real time multiplayer games
Scalability & Big Data challenges in real time multiplayer games
 
Let s Enjoy Node.js
Let s Enjoy Node.jsLet s Enjoy Node.js
Let s Enjoy Node.js
 
Node lt
Node ltNode lt
Node lt
 
Defcon CTF quals
Defcon CTF qualsDefcon CTF quals
Defcon CTF quals
 
NetRacer for the Commodore 64
NetRacer for the Commodore 64NetRacer for the Commodore 64
NetRacer for the Commodore 64
 
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
TDC2017 | São Paulo - Trilha Programação Funcional How we figured out we had ...
 
Akka for realtime multiplayer mobile games
Akka for realtime multiplayer mobile gamesAkka for realtime multiplayer mobile games
Akka for realtime multiplayer mobile games
 
Mobile webapplication development
Mobile webapplication developmentMobile webapplication development
Mobile webapplication development
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_js
 
Game Programming - Cloud Development
Game Programming - Cloud DevelopmentGame Programming - Cloud Development
Game Programming - Cloud Development
 
Multiplayer Java Game
Multiplayer Java GameMultiplayer Java Game
Multiplayer Java Game
 
Building fast,scalable game server in node.js
Building fast,scalable game server in node.jsBuilding fast,scalable game server in node.js
Building fast,scalable game server in node.js
 
RandomGuessingGame
RandomGuessingGameRandomGuessingGame
RandomGuessingGame
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_js
 
Clojure ♥ cassandra
Clojure ♥ cassandra Clojure ♥ cassandra
Clojure ♥ cassandra
 
On Space-Scarce Economy In Blockchain Systems
On Space-Scarce Economy In Blockchain SystemsOn Space-Scarce Economy In Blockchain Systems
On Space-Scarce Economy In Blockchain Systems
 
Game server development in node.js in jsconf eu
Game server development in node.js in jsconf euGame server development in node.js in jsconf eu
Game server development in node.js in jsconf eu
 
Building Multiplayer Games (w/ Unity)
Building Multiplayer Games (w/ Unity)Building Multiplayer Games (w/ Unity)
Building Multiplayer Games (w/ Unity)
 
Monte Carlo C++
Monte Carlo C++Monte Carlo C++
Monte Carlo C++
 

Recently uploaded

MCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdfMCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdf
Osamah Alsalih
 
Final project report on grocery store management system..pdf
Final project report on grocery store management system..pdfFinal project report on grocery store management system..pdf
Final project report on grocery store management system..pdf
Kamal Acharya
 
block diagram and signal flow graph representation
block diagram and signal flow graph representationblock diagram and signal flow graph representation
block diagram and signal flow graph representation
Divya Somashekar
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
gerogepatton
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
Jayaprasanna4
 
power quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptxpower quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptx
ViniHema
 
The role of big data in decision making.
The role of big data in decision making.The role of big data in decision making.
The role of big data in decision making.
ankuprajapati0525
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
Intella Parts
 
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdfCOLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
Kamal Acharya
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
gdsczhcet
 
ethical hacking-mobile hacking methods.ppt
ethical hacking-mobile hacking methods.pptethical hacking-mobile hacking methods.ppt
ethical hacking-mobile hacking methods.ppt
Jayaprasanna4
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
Amil Baba Dawood bangali
 
Vaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdfVaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdf
Kamal Acharya
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
karthi keyan
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
FluxPrime1
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
bakpo1
 
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdfTop 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Teleport Manpower Consultant
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
Kamal Acharya
 
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
MLILAB
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
AhmedHussein950959
 

Recently uploaded (20)

MCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdfMCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdf
 
Final project report on grocery store management system..pdf
Final project report on grocery store management system..pdfFinal project report on grocery store management system..pdf
Final project report on grocery store management system..pdf
 
block diagram and signal flow graph representation
block diagram and signal flow graph representationblock diagram and signal flow graph representation
block diagram and signal flow graph representation
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
 
power quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptxpower quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptx
 
The role of big data in decision making.
The role of big data in decision making.The role of big data in decision making.
The role of big data in decision making.
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
 
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdfCOLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
COLLEGE BUS MANAGEMENT SYSTEM PROJECT REPORT.pdf
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
 
ethical hacking-mobile hacking methods.ppt
ethical hacking-mobile hacking methods.pptethical hacking-mobile hacking methods.ppt
ethical hacking-mobile hacking methods.ppt
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
 
Vaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdfVaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdf
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
 
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdfTop 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
 
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
 

NodeAtlanta 2020 - Making Multiplayer Games with Colyseus and Node.js.pptx

  • 1. Making Online Multiplayer Game Experiences With Colyseus and Node.js This presentation is from Jan 2020. It was supposed to happen on Node Atlanta 2020, but unfortunately it got cancelled.
  • 2. Hi, I’m Endel 👋 👨💻 Software Engineer and Game Developer from Brazil 🎓 Game Development & Digital Entertainment GitHub / Twitter: @endel I DON’T LIKE SOCCER
  • 3. Summary ● Introduction ● Basic concepts ○ How Online Multiplayer Games Work ○ Authoritative Game Servers ● Colyseus in-depth ○ Match-making ○ Game State & Serialization ○ Infrastructure ● Client-side Techniques
  • 4. A naive beginning (2015) ● Socket.io ● Not authoritative ● Synchronization issues between clients
  • 5. Server Broadcasts messages back to clients Client 1. State Client 2. State Client 3. State 100ms 100ms 150ms A naive beginning (2015) Not authoritative
  • 6. A naive beginning (2015) All I wanted ● Be able to handle multiple game sessions ● Manipulate data structures in the server ○ Have them automatically synchronized with the clients
  • 8. Why Colyseus? ● Multiplayer games should be easier to make ● Should be more accessible ● Should work on as many platforms as possible ● Should be open-source!
  • 9. Client-side Integration ● HTML5 (JavaScript / TypeScript) ● Unity3D (C#) ● Defold Engine (LUA) ● Haxe ● Cocos2d-X (C++)
  • 10. How Online Multiplayer Games Work? ● Peer-to-peer ● Client as a host ● Hybrid (of two above) ● Client / Server Server Dumb client Dumb client Dumb client
  • 11. Responsibilities of the Server ● Hold the Game State ● Hold the Game Logic ● Validate client inputs ● Send the Game State to the clients Server Dumb client Dumb client Dumb client
  • 12. Responsibilities of the Client ● Visual representation ● Apply game state updates in a pleasant manner ● Send inputs (actions) to the Server Server Dumb client Dumb client Dumb client
  • 13. Authoritative Game Servers My new position is now [x, y] *mxlvl.com: This game was made by @x100
  • 14. Authoritative Game Servers My new position is now [x, y] *mxlvl.com: This game was made by @x100
  • 15. Authoritative Game Servers I’m pointing at angle X and moving forward *mxlvl.com: This game was made by @x100
  • 16. Authoritative Game Servers I’m pointing at angle X and moving forward *mxlvl.com: This game was made by @x100
  • 17. Colyseus ● Uses HTTP/WebSockets ● Game Rooms ● Match-making ● Room State Serialization ● Scalability*
  • 19. Colyseus Game Rooms The match-maker has instantiated this room. Rooms are in-memory, and live on a particular Node.js process
  • 20. Colyseus Game Rooms Client is asking to join into this room
  • 21. Colyseus Game Rooms Client sent a message to this room
  • 22. Colyseus Game Rooms Client has been disconnected (or left intentionally)
  • 23. Colyseus Game Rooms The room is being destroyed. (It’s a good place to persist things on the database) Rooms are disposed automatically when the last client disconnects (unless autoDispose=false)
  • 26. Colyseus Game Rooms ● Rooms are created during matchmaking ● A client can be connected to multiple rooms ● Each room connection has its own WebSocket connection
  • 27. ● client.join( “game”, { options } ) ● client.create( “game”, { options } ) ● client.joinOrCreate( “game”, { options } ) ● client.joinById( roomId, { options } ) ● client.reconnect( roomId, sessionId ) Colyseus Matchmaking (Client-side)
  • 28. Colyseus Matchmaking (Client-side) try { const room = await client.joinOrCreate("game", { name: "Jake Badlands" }); } catch (e) { console.log("Could not join!"); console.error(e); }
  • 29. Colyseus Matchmaking ient.joinOrCreate("game", { s" t join!"); class MyRoom extends Room { maxClients = 4; async onAuth(client, options) { // retrieve user data from auth provider return true; } onJoin(client, options) { // mutate the this.state console.log(options.name, "joined!"); } } Client-side: Server-side:
  • 30. http: request seat reservation roomId / sessionId WebSocket request Connection established Colyseus Matchmaking
  • 31. Full Room’s State ... State patches Connection established Colyseus Room State & Serialization ... State patches
  • 32. ● The Room State is MUTABLE ● Patches are broadcasted at every 50ms ○ Customizable via this.setPatchRate(ms) ( But first, a bit of a background… ) Colyseus Room State & Serialization
  • 33. 👎 Serialization: back in v0.1 ~ v0.3 👎 ● Deep JavaScript Object Comparison ● JSON Patch (RFC6902) ● Patch size is too large [ { "op": "remove", "path": "/players/N150OHMve" }, { "op": "add", "path": "/players/NkfeqSGPx", "value": {"x": 10, "y": 10} }, { "op": "replace", "path": "/players/NkfeqSGPx/x", "value": 5 } ] Previous State ⇔ Current State
  • 34. 👎 Serialization: back in v0.4 ~ v0.9 👎 ● “Fossil Delta” Binary Diff Compression ● Hard to detect a change in the state ● Creates a new copy when decoding var currentState = this.state; var currentStateEncoded = msgpack.encode( currentState ); // skip if state has not changed. if ( currentStateEncoded.equals( this._previousStateEncoded ) ) { return false; } var patches = delta.create(this._previousStateEncoded, currentStateEncoded); CPU Intensive
  • 35. Serialization: v0.10+ ● @colyseus/schema ● Schema-based ● Strongly-typed ● Binary serialization ● Inspired by protobuf / flatbuffers / avro
  • 36. Serialization: v0.10+ ● @colyseus/schema ● Schema-based ● Strongly-typed ● Binary serialization ● Inspired by protobuf / flatbuffers / avro
  • 37. 🔲 Encode/Decode only fields that have changed 🔲 No bottleneck to detect state changes 🔲 Mutations should be cheap 🔲 Avoid decoding large objects that haven't been patched 🔲 Better developer experience on statically-typed languages (C#, C++) Serialization: v0.10+ Checklist
  • 38. ✅ Encode/Decode only fields that have changed ✅ No bottleneck to detect state changes ❓ Mutations should be cheap (need more benchmarks, so far seems ✅) ✅ Avoid decoding large objects that haven't been patched ✅ Better developer experience on statically-typed languages (C#, C++) Serialization: v0.10+ Checklist
  • 39. class Player extends Schema { @type("number") x: number; @type("number") y: number; } const player = new Player(); player.x = 10; player.y = 20; player.encode() [ 0, 10, 1, 20 ] Definition Usage Serialization: v0.10+ Demo
  • 40. class Player extends Schema { @type("number") x: number; @type("number") y: number; } const player = new Player(); player.x = 10; player.y = 20; player.encode() [ 0, 10, 1, 20 ] Definition Usage Serialization: v0.10+ Demo
  • 41. class Player extends Schema { @type("number") x: number; @type("number") y: number; } const player = new Player(); player.x = 10; player.y = 20; player.encode() [ 0, 10, 1, 20 ] Definition Usage Serialization: v0.10+ Demo
  • 43. Colyseus Room State & Serialization import { Schema, type, MapSchema } from "@colyseus/schema"; class Player extends Schema { @type("number") position: number = 0; } class GameState extends Schema { @type({ map: Player }) players = new MapSchema<Player>(); } Game State Example
  • 44. class MyRoom extends Room { onCreate(options) { this.setState(new GameState()); } onJoin(client, options) { this.state.players[client.sessionId] = new Player(); } onMessage(client, message) { if (message.move) { this.state.players[client.sessionId].position++; } Colyseus Room State & Serialization Room State Example
  • 45. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.onStateChange((state) => { console.log("You are:", state.players[ room.sessionId ]) }); Client-side: Receiving Patches
  • 46. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.onStateChange((state) => { console.log("You are:", state.players[ room.sessionId ]) }); Client-side: Receiving Patches What exactly has changed?
  • 47. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.state.players.onAdd = (player, sessionId) => { console.log("player joined", sessionId); player.listen("position", (position) => { console.log("position changed", position); }); } room.state.players.onRemove = (player, sessionId) { console.log("player left", sessionId); }
  • 48. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.state.players.onAdd = (player, sessionId) => { console.log("player joined", sessionId); player.listen("position", (position) => { console.log("position changed", position); }); } room.state.players.onRemove = (player, sessionId) { console.log("player left", sessionId); } Player joined Player left
  • 49. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.state.players.onAdd = (player, sessionId) => { console.log("player joined", sessionId); player.listen("position", (position) => { console.log("position changed", position); }); } room.state.players.onRemove = (player, sessionId) { console.log("player left", sessionId); } Particular property changes
  • 50. Colyseus Room State & Serialization const room = await client.joinOrCreate<GameState>("game"); room.state.players.onAdd = (player, sessionId) => { console.log("player joined", sessionId); player.listen("position", (position) => { console.log("position changed", position); }); } room.state.players.onRemove = (player, sessionId) { console.log("player left", sessionId); } Autocompletion 💖 TypeScript 💖
  • 51. Colyseus Room State & Serialization const room = await client.joinOrCreate("game"); room.state.players.onAdd = (player, sessionId) => { console.log("player joined", sessionId); player.listen("position", (position) => { console.log("position changed", position); }); } room.state.players.onRemove = (player, sessionId) { console.log("player left", sessionId); } (Consistency?!)
  • 52. ● By default, the entire state is sent to everyone ○ This way no “private” data can be stored in the state ● @filter() let you define a filter callback per property, per client Colyseus State Filters! (Experimental)
  • 53. Colyseus State Filters! (Experimental) class Entity extends Schema { @filter(function(client, value, root) { // this = instance of Entity // client = the client which this value is being filtered for // value = `secret` value // root = the Root State return false; }) @type("number") secret: number; }
  • 54. Colyseus State Filters! (Experimental) class Entity extends Schema { @filter(function(client, value, root) { // this = instance of Entity // client = the client which this value is being filtered for // value = `secret` value // root = the Root State return false; }) @type("number") secret: number; }
  • 55. Colyseus State Filters! (Experimental) class Entity extends Schema { @filter(function(client, value, root) { // this = instance of Entity // client = the client which this value is being filtered for // value = `secret` value // root = the Root State return false; }) @type("number") secret: number; }
  • 56. Colyseus State Filters! (Experimental) class Card extends Schema { @filter(function(this: Card, client, value, root?: RootState) { return root. players[client.sessionId]. cards.indexOf(this) !== -1; }) @type("number") number: number; } Card Game Example: Only Card owners can see their own Card data
  • 57. Colyseus State Filters! (Experimental) class Player extends Schema { sessionId: string; @filter(function(this: Player, client: any, value: Card) { return this.sessionId === client.sessionId; }) @type([Card]) cards = new ArraySchema<Card>(); } ⚠️ Array and Map filters are not currently supported ⚠️ (Planned for version 1.0)
  • 58. Colyseus State Filters! (Experimental) class State extends Schema { @filter(function(this: State, client: any, value: Player) { const player = this.players[client.sessionId] const a = value.x - player.x; const b = value.y - player.y; return (Math.sqrt(a * a + b * b)) <= 10; }) @type({ map: Player }) players = new MapSchema<Player>(); } ⚠️ Array and Map filters are not currently supported ⚠️ (Planned for version 1.0)
  • 59. using Colyseus.Schema; public class State : Schema { [Type("string")] public string fieldString = ""; [Type("number")] public float fieldNumber = 0; [Type("ref", typeof(Player))] public Player player = new Player( [Type("array", typeof(ArraySchema< public ArraySchema<Player> arrayOf ArraySchema<Player>(); [Type("map", typeof(MapSchema<Play Client-side integration (C#) ● npx schema-codegen State.ts --csharp --output client-state generated: State.cs Colyseus Room State & Serialization
  • 60. using namespace colyseus::schema; class State : public Schema { public: string fieldString = ""; varint_t fieldNumber = 0; Player *player = new Player(); ArraySchema<Player*> *arrayOfPlaye ArraySchema<Player*>(); MapSchema<Player*> *mapOfPlayers = State() { this->_indexes = {{0, "fiel this->_types = {{0, "strin this->_childPrimitiveTypes Client-side integration (C++) ● npx schema-codegen State.ts --cpp --output client-state generated: State.hpp Colyseus Room State & Serialization
  • 61. import io.colyseus.serializer.schema.Schema; class State extends Schema { @:type("string") public var fieldString: String = "" @:type("number") public var fieldNumber: Dynamic = 0 @:type("ref", Player) public var player: Player = new Pla @:type("array", Player) public var arrayOfPlayers: ArraySch Client-side integration (Haxe) ● npx schema-codegen State.ts --haxe --output client-state generated: State.hx Colyseus Room State & Serialization
  • 62. Colyseus Sending Messages ● Messages use MsgPack by default room.onMessage((message) => { console.log("received:", message) }) room.send({ action: "hello" }) Client-side onMessage(client, message) { if (message.action === "hello") { this.broadcast("world!"); this.send(client, "world!"); } } Server-side
  • 63. Colyseus Sending Messages ● Messages use MsgPack by default room.onMessage((message) => { console.log("received:", message) }) room.send({ action: "hello" }) Client-side onMessage(client, message) { if (message.action === "hello") { this.broadcast("world!"); this.send(client, "world!"); } } Server-side Send message to everyone
  • 64. Colyseus Sending Messages ● Messages use MsgPack by default room.onMessage((message) => { console.log("received:", message) }) room.send({ action: "hello" }) Client-side onMessage(client, message) { if (message.action === "hello") { this.broadcast("world!"); this.send(client, "world!"); } } Server-side Send message to a single client
  • 65. Colyseus Handling reconnection ● What if one of my clients drops the connection? ○ Closed/Refreshed Browser ○ Switched from Wifi / 3G ○ Unstable Internet Connection ○ Etc.
  • 66. class MyRoom extends Room { async onLeave (client, consented: boolean) { try { if (consented) { throw new Error("consented leave"); } await this.allowReconnection(client, 20); console.log("Client successfully reconnected!"); } catch (e) { console.log("Could not reconnect."); } } Colyseus Handling reconnection (Server-side)
  • 67. class MyRoom extends Room { async onLeave (client, consented: boolean) { try { if (consented) { throw new Error("consented leave"); } await this.allowReconnection(client, 20); console.log("Client successfully reconnected!"); } catch (e) { console.log("Could not reconnect."); } } Colyseus Handling reconnection (Server-side) room.leave() was called from the client
  • 68. class MyRoom extends Room { async onLeave (client, consented: boolean) { try { if (consented) { throw new Error("consented leave"); } await this.allowReconnection(client, 20); console.log("Client successfully reconnected!"); } catch (e) { console.log("Could not reconnect."); } } Colyseus Handling reconnection (Server-side) Hold client’s seat reservation (sessionId) for 20 seconds
  • 69. class MyRoom extends Room { async onLeave (client, consented: boolean) { try { if (consented) { throw new Error("consented leave"); } await this.allowReconnection(client, 20); console.log("Client successfully reconnected!"); } catch (e) { console.log("Could not reconnect."); } } Colyseus Handling reconnection (Server-side) No reconnection in 20 seconds, promise rejected
  • 70. class MyRoom extends Room { async onLeave (client, consented: boolean) { try { if (consented) { throw new Error("consented leave"); } await this.allowReconnection(client, 20); console.log("Client successfully reconnected!"); } catch (e) { console.log("Could not reconnect."); } } Colyseus Handling reconnection (Server-side) Reconnected!
  • 71. const room = await client.joinOrCreate("game", {}); // Cache roomId and sessionId localStorage.setItem("lastRoomId", room.id); localStorage.setItem("lastSessionId", room.sessionId); // (CLOSE BROWSER TAB) const lastRoomId = localStorage.getItem("lastRoomId"); const lastSessionId = localStorage.getItem("lastSessionId"); const room = await client.reconnect(lastRoomId, lastSessionId); Colyseus Handling reconnection (Client-side)
  • 72. const room = await client.joinOrCreate("game", {}); // Cache roomId and sessionId localStorage.setItem("lastRoomId", room.id); localStorage.setItem("lastSessionId", room.sessionId); // (CLOSE BROWSER TAB) const lastRoomId = localStorage.getItem("lastRoomId"); const lastSessionId = localStorage.getItem("lastSessionId"); const room = await client.reconnect(lastRoomId, lastSessionId); Colyseus Handling reconnection (Client-side)
  • 73. Client-side Techniques Client Server p = [10, 10] p = [10, 10] p = [11, 10] p = [11, 10] Delay (~100ms)
  • 74. Client-side Techniques Client Server p = [10, 10] p = [10, 10] p = [11, 10] p = [11, 10] Delay (~100ms)
  • 75. Client-side Techniques ● Linear Interpolation (Lerp) https://mazmorra.io tick() { sprite.x = lerp(sprite.x, serverX, 0.07); sprite.y = lerp(sprite.y, serverY, 0.07); }
  • 76. Client-side Techniques ● Delay to process actions ● Simulation on other client happens 1 second later https://poki.com/en/g/raft-wars-multiplayer
  • 77. Client-side Techniques ● Client-side prediction ● Process player’s input immediately ● Enqueue player’s actions in the server ● Process the queue at every server tick https://github.com/halftheopposite/tosios
  • 78. Infrastructure ● How many CCU can Colyseus handle? ○ It depends! ● You have CPU and Memory limits ○ Optimize Your Game Loop ● 1000 clients in a single room? ○ Probably no! ● 1000 clients distributed across many rooms? ○ Probably yes!
  • 81. Scalability ● Colyseus’ rooms are STATEFUL ● Games are (generally) STATEFUL ● Rooms are located on a specific server and/or process.
  • 83. Colyseus Communication between Game Rooms onCreate() { /** * Pub/Sub */ this.presence.subscribe("global-action", (data) => { console.log(data); }); this.presence.publish("global-action", "hello!"); /** * "Remote Procedure Call" */ matchMaker.remoteRoomCall(roomId, "methodName", [...]) }
  • 85. Colyseus Tools @colyseus/loadtest $ npx colyseus-loadtest bot.ts --room my_room --numClients 10
  • 86. ● Realtime ● Phaser/JavaScript ● Built by @tinydobbings ● ~350 concurrent players Built with Colyseus GunFight.io https://gunfight.io
  • 87. ● Turn-based ● JavaScript version ● Defold Engine version (@selimanac) Built with Colyseus Tic-Tac-Toe https://github.com/endel/colyseus-tic-tac-toe
  • 88. ● “The Open-Source IO Shooter” ● Realtime shooter ● PixiJS / TypeScript ● Built by @halftheopposite Built with Colyseus TOSIOS https://github.com/halftheopposite/tosios
  • 89. ● Turn-based ● Defold Engine / LUA ● ~250 concurrent players ● (soon on mobile!) Built with Colyseus Raft Wars Multiplayer https://poki.com/en/g/raft-wars-multiplayer
  • 90. ● Instant Game (Messenger) ● Realtime Built with Colyseus Chaos Shot https://www.facebook.com/ChaosShot.io/
  • 91. Plans for v1.0 ● Better State Filters (on Arrays and Maps) ● Allow more sophisticated matchmaking ● Transport-independent (TCP/UDP) ● ...while keeping all client-side integrations up-to-date!
  • 92. Thank You! ❤ ● Twitter/GitHub: @endel

Editor's Notes

  1. This game was made using Colyseus by @x100, a community member
  2. This is how a room definition looks like All these methods can be async
  3. I’ll talk a bit about state serialization before going into handling the game state I hope it’s not going to be too much information, because it’s kind of a deep topic
  4. Fossil Delta is really good for small binary diffs CPU Intensive detecting changes Explain client-side new allocations and garbage collection
  5. Why would you re-implement these great tools Incremental encoding
  6. This is the checklist I had before going ahead and implementing the serializer
  7. Because we’re using Proxies to be able to catch changes on the state, mutations have this cost
  8. The .encode() method is just to illustrate how it works, you usually don’t need to call it manually, as the room does automatically at the patch interval
  9. The .encode() method is just to illustrate how it works, you usually don’t need to call it manually, as the room does automatically at the patch interval
  10. The .encode() method is just to illustrate how it works, you usually don’t need to call it manually, as the room does automatically at the patch interval
  11. Has been released early last year I’ve a good chunk of time last year fixing bugs on it, and improving things
  12. I’ll talk a bit about state serialization before going into handling the game state
  13. During onStateChange, you can’t really know exactly what has changed
  14. During onStateChange, you can’t really know exactly what has changed
  15. Mention that “player” references can be passed around, as it is going to be always the same
  16. Here you actually get autocompletion for the field, and the callback will have the right type you’re listening for
  17. Filters are experimental, because their performance is not that great currently
  18. This is the signature of the @filter callback
  19. Delays are
  20. This is a trickier one. Mention turn-based
  21. This is a trickier one.