Does Colyseus Support Host-Client Online Mode ?

For example, a Colyseus client is a room host, others can join the room. The dedicated server just relay the state between host and client.

Can the schema-based state synchronization apple to the above situation?

hi @stromkuo , that's not the way it works, clients can join or create rooms but not "host" a room.
The way you could achieve something like that would be with a custom implementation where you allow only an specific client to create rooms and the other ones only join those rooms if were already created.
Also, you could save in the room metadata which user create it and give it more "features" than to the other joined clients, but again Colyseus by default will handle the server-client communication and sync, everything else will be on your own.

Hi @StromKuo, welcome!

If you're going to just relay the messages, I don't think you'd use the state at all. I actually have a room handler that I wanted to provide in the core of Colyseus in case someone need this, but I haven't tested or experimented with it yet. You can try it out and adapt to your needs though 😅

import { MapSchema, Schema, type } from '@colyseus/schema';

import { Client } from '.';
import { Room } from './Room';

class Player extends Schema {
    public id: string;

    public connected: boolean;

    public isMaster: boolean;

class State {
    @type({ map: Player })
    public players = new MapSchema<Player>();

 * client.joinOrCreate("punroom", {
 *   maxClients: 10,
 *   allowReconnectionTime: 20
 * });

export class PUNRoom extends Room<State> {
    public allowReconnectionTime: number = 0;

    public onCreate(options) {
        this.setState(new State());

        if (options.maxClients) {
            this.maxClients = options.maxClients;

        if (options.allowReconnectionTime) {
            this.allowReconnectionTime = Math.min(options.allowReconnectionTime, 40);

        if (options.metadata) {

    public onJoin(client: Client, options: any) {
        const player = new Player();

        // first player joining is assigned as master
        player.isMaster = (this.clients.length === 1);

        if (this.allowReconnectionTime > 0) {
            player.connected = true;

        this.state.players[client.sessionId] = player;

    public onMessage(client: Client, message: any) {
        message.sessionId = client.sessionId;
        this.broadcast(message, { except: client });

    public async onLeave(client: Client, consented: boolean) {
        // master is leaving, let's assign a new master.
        if (this.state.players[client.sessionId].isMaster) {
            const availableSessionIds = Object.keys(this.state.players).filter((sessionId) => sessionId !== client.sessionId);
            const newMasterSessionId = availableSessionIds[Math.floor(Math.random() * availableSessionIds.length)];
            this.state.players[newMasterSessionId].isMaster = true;

        if (this.allowReconnectionTime > 0) {
            this.state.players[client.sessionId].connected = false;

            try {
                if (consented) {
                    throw new Error('consented leave');

                await this.allowReconnection(client, this.allowReconnectionTime);
                this.state.players[client.sessionId].connected = true;

            } catch (e) {
                delete this.state.players[client.sessionId];


I've made some slight changes to this room and made it available here:

It is not exposed in the colyseus module yet, maybe eventually it will!