2025
Satos
A live multiplayer game I built end to end — client, server, admin.
I built Satos as a complete multiplayer game, not a client demo. Two players — Bull and Bear — fight over 25 territories. Combat is a quiz. Around that I shipped character customisation, regional themes, a live WebSocket match, and the admin tools you need to run it.
A match is about 15 minutes: lobby, spreading over empty land, then conquering neighbours, until a base falls or turns run out. I implemented that as stages on the server, not as one giant room class.
What I owned
- Unity client — one project targeting WebGL, iOS, Android, and desktop
- Node backend — Express for accounts, Colyseus for the match
- Admin — a React dashboard, plus a trait packager for cosmetics
- Deploy — the whole stack comes up with
docker compose up
dobab/
├── Satos/ # Unity game client (C#)
├── Backend/ # Node.js API + game server
├── AdminUI/ # React admin dashboard
├── AdminTPM/ # Trait Package Manager
└── Deployment/ # Docker, nginx
Realtime, on purpose
The match server is a sequence of Stage classes. Lobby only handles ready, side select, and stakes. Spreading and conquer do not share that code. Client and server talk through a numeric protocol grouped by phase, so a typo is a compile problem, not a silent desync.
export class Lobby extends Stage {
onInit() {
this.countdownTimer = this.settings.COUNTDOWN_FULL_SEC
this.game.onMessage(ClientMessage.LOBBY_READY, this.onPlayerReady)
this.game.onMessage(ClientMessage.LOBBY_SELECTSIDE, this.onPlayerSelectSide)
this.game.onMessage(ClientMessage.LOBBY_STAKE_OFFER, this.onPlayerStakeOffer)
this.game.onMessage(ClientMessage.LOBBY_STAKE_RESPONSE, this.onPlayerStakeResponse)
}
}

I did not want a balance tweak to mean a new build. Almost every gameplay number — XP, quiz timers, lobby countdown, turn limit — lives in the database with a default and a description. Operators change it from the admin panel. The game picks it up at runtime.

The admin is two tools I built for running a live game: a dashboard for accounts, config, the question bank, mail, and server health, and a YAML-based packager for hair, skins, and the rest of the cosmetics.

If you are reading this next to a CV: this is the project where I had to hold a game client, a realtime server, a REST API, and ops UI in one head — and make them agree.
| Piece | What I used |
|---|---|
| Game client | Unity 6000 |
| Backend | Node.js |
| WebSocket | Colyseus |
| REST | Express.js |
| Database | SQLite + Sequelize |
| Admin | React + Vite |
| Deploy | Docker |