This is a template repo to create game servers for oo.gg games
-
run
npm i
in the root folder of the project to insall the dependencies. -
in
partykit.json
setname
field to change the project's name -
run
npm run dev
to test, the game server will start onhttp://127.0.0.1:1999
. You can set this address in the client script to connect to the room. -
When you're ready to publish the server code. run
npm run deploy
, this will give you an online address, you can then replace the local address in the client script.
The room state is defined in src/types
folder; The default template includes a preset for players and a game timer.
You can extend the template by adding properties to the relevent class. Every property must be annotated with
a @type(...)
. This is needed so that the room server can efficiently serialize the state over the network.
Currently we use @colyseus/schema
package for state definition and serialization. See https://docs.colyseus.io/state/schema/ for more information.
To add player specific attributes, add the relevent properties to the Player
class.
To add general game attributes, add the relevent properties to the RoomState
class.
The state is synchronized regularly for all client scripts; You can customize the rate in src/room.ts
by setting the tickRate
property;
It's important to not delete the current state properties defined in this template (like players
...); they're used internally by the multiplayer package
The room behavior is defined in src/room.ts
.
You can customize the room behavior by setting room properties and implementing some callback methods
export class MyRoom extends GameRoom<RoomState> {
// Properties
tickRate = 30
// ...
// Callback methods
onPreload() { ... }
onJoin(player) { ... }
onMessage(message, player) { ... }
// ...
}
Let's look on how to customize room class
-
tickRate
: defines how often the state is synchronized with the clients -
state
: room state -
maxPlayers
: max number of players that can join the room -
simulatedLatency
: in milliseconds, used to simulate network latency when using local dev server -
readonly
status
: Game loop status, can be"idle"
or"running"
.
-
onPreload()
: is called before any client connection is made. Use this to define room initialization that needs to run only once. -
onJoin(player)
: called when a player has beed added to the room. Use this method to initialize the player state like spawn position ... -
onLeave(player)
: called when a player has left the room. -
onRequestStart
: called when the room host request a game start; the default implementation. You can call thestartGame(countdown)
here to notify clients that the game will start incountdown
seconds. -
onMessage(message, player)
: When a message is received from a client script. -
onUpdate(dt)
: called on each tick of the game loop. This is only invoked after you invokestartGame
. -
onDispose
: called when the room is disposed. The room is disposed when all players has left.
-
broadcast (msg, except?)
: Sends a message to all players, You can exclude some in the seconds argument (player ids) -
send (msg, playerId)
: Sends a message to a single client -
startGame(countdown)
: Starts the game aftercountdown
seconds. And notify clients, taking into account the nework latency for each player. This is usually called in theonRequestStart
handler method. -
stopGame()
: Stops the game loop, and notifies all the client. Call this when the game ends by reaching the max time, or reaching a win/lose condition.
import { GameClient } from "@oo/scripting"
const host = "http://127.0.0.1:1999"
// or const host = your deployement url (got from npm run deploy)
class GameManager {
async onPreload() {
this.room = GameClient.join({ host })
await this.room.ready
// If you want to synchronize the players state (position, rotation, animation)
this.playerSync = this.room.getPlayerStateSync()
}
onUpdate(dt: number) {
this.playerSync.update(dt)
}
}
this.room.send({ type: "hit", target: "myTarget" })
You can then handle this message in the server room. For example:
export class MyRoom extends GameRoom<RoomState> {
// ...
onMessage(message, player) {
if (message.type === "hit") {
player.points++
}
}
// ...
}
-
room.onConnect(() => {...})
: called when the room is connected -
room.onDisconnect(() => {...})
: called when the room is connected -
room.onStart((countdown) => {...})
: called when the room receives a start game notification from the server -
room.onStop(() => {...})
: called when the room receives a stop game notification from the server -
room.onPlayerJoined((player) => { ... })
: called when a new player joins the room -
room.onPlayerLeft((player) => { ... })
: called when the room is connected
-
room.send(msg)
: sends a message to the server room -
room.requestStart()
: sends a start game request to the server; only the room host is allowed to send a start game request -
room.leave()
: disconnects from the server room -
readonly
room.state
: State of the room as defined in the server; the state is synchronized following the defined tick rate of the room. Only the server can change the room state; to initiate a change from client side, you must do through sending messages. -
readonly
room.ready
: promise that resolves when the room is connected and properly synced with the server -
readonly
room.isHost
: true is the player is the room's host -
readonly
room.roomId
: id of the room -
readonly
room.sessionId
: current player session id -
readonly
room.tickRate
: tick rate of the server room -
room.getPlayerSync()
: returns a helper object to sync all players states (position, rotation and animation); the object sendsplayer-state
messages regularly to the server to notify of local player changes; and sync remote player state with the 3D scene using snapshot interpolation.
Client side, you must call playerSync.update(dt)
on the update loop
Server side, the default template handles the player-state
message by updating player state on the server room. You can sync other proeprties in the updatePlayerState() {}
method.
Currently the player state sync is not server authoritative, so it's not well suited to fast paced contact games that involves precise collision handling. Server authoritative handling is a WIP