DFlike

A multiplayer browser game inspired by Dwarf Fortress. Watch autonomous NPCs with needs-driven AI wander, eat, and rest in a shared world. Connect as an observer or take control of an avatar.

Architecture

  • Server: Node.js + Socket.io, server-authoritative ECS simulation at 10 ticks/sec
  • Client: Phaser 3 + TypeScript, Vite bundler
  • Shared: TypeScript types and constants used by both server and client
dflike/
  shared/    -- Types and constants (no runtime code)
  server/    -- Node.js + Socket.io game server with ECS
  client/    -- Phaser 3 + TypeScript renderer
  chars/     -- Character sprite assets
  docs/      -- Design and implementation docs

Prerequisites

  • Node.js >= 20 (tested with v22)
  • npm >= 10

Quick Start (Development)

npm install

# Copy character assets into the client public directory
cp -r chars client/public/assets/

# Terminal 1: Start server (port 3001)
npm -w server run dev

# Terminal 2: Start client dev server (port 3000)
npm -w client run dev

Open http://localhost:3000. Open multiple tabs for multiplayer.

Controls

  • WASD / Arrow Keys -- pan camera (camera mode), move avatar (avatar mode), or cycle NPCs (follow mode)
  • TAB -- toggle between camera, avatar, and follow modes

Follow Mode

In follow mode, the camera tracks an NPC and a panel slides in from the right showing:

  • Composited character portrait (layered from skin + accessories + facial features)
  • NPC name (randomly generated fantasy names)
  • Current activity (Wandering, Eating, Resting, Idle)
  • Live-updating needs bars (Hunger, Energy) with color-coded fill levels

The panel uses an EarthBound/SNES RPG-inspired aesthetic with doubled borders, pixel font, and smooth slide transitions.

Deployment on Debian LXC

These instructions cover running DFlike on a headless Debian (or Ubuntu) LXC container, serving the game on your local network.

1. System Setup

apt update && apt upgrade -y
apt install -y curl git

2. Install Node.js

Install Node.js 22.x via NodeSource:

curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt install -y nodejs
node --version  # should print v22.x.x

3. Clone and Install

cd /opt
git clone <your-repo-url> dflike
cd dflike
npm install

4. Copy Character Assets

The character sprite assets in chars/ need to be copied into the client's public directory. They are excluded from the client build via .gitignore.

cp -r chars client/public/assets/

5. Build the Client

By default the client connects to the game server on the same hostname it was loaded from (port 3001). To override, set the VITE_SERVER_URL environment variable before building:

# Default: auto-detects from browser hostname (works for most setups)
npm -w client run build

# Or specify an explicit server URL
VITE_SERVER_URL=http://192.168.1.50:3001 npm -w client run build

For development, you can also create a client/.env.local file:

VITE_SERVER_URL=http://192.168.1.50:3001

6. Serve the Client Build

Install a simple static file server to serve the built client:

npm install -g serve

Or use nginx (recommended for production):

apt install -y nginx

nginx config (/etc/nginx/sites-available/dflike):

server {
    listen 80;
    server_name _;

    root /opt/dflike/client/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Enable and restart:

ln -s /etc/nginx/sites-available/dflike /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl restart nginx

7. Run the Game Server

Quick start:

cd /opt/dflike
npm -w server run start

The server listens on port 3001 by default. Override with the PORT environment variable:

PORT=4000 npm -w server run start

8. Run as a systemd Service

Create /etc/systemd/system/dflike.service:

[Unit]
Description=DFlike Game Server
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/dflike
ExecStart=/usr/bin/npx tsx server/src/main.ts
Restart=on-failure
RestartSec=5
Environment=PORT=3001
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Enable and start:

systemctl daemon-reload
systemctl enable dflike
systemctl start dflike
systemctl status dflike   # verify it's running
journalctl -u dflike -f   # view logs

9. Firewall

If you have a firewall enabled, open the necessary ports:

# Game server (Socket.io)
ufw allow 3001/tcp

# Web server (nginx)
ufw allow 80/tcp

Deploying with Traefik (Reverse Proxy)

If you're exposing the game through Traefik on a domain (e.g. game.example.com), you need two routes: one for the static client and one for the Socket.io game server.

Traefik dynamic config (e.g. game.yaml):

http:
  routers:
    game-ws:
      rule: "Host(`game.example.com`) && PathPrefix(`/socket.io`)"
      entryPoints:
        - websecure
      service: game-ws
      tls:
        certResolver: letsencrypt
    game:
      rule: "Host(`game.example.com`)"
      entryPoints:
        - websecure
      service: game
      tls:
        certResolver: letsencrypt
  services:
    game:
      loadBalancer:
        servers:
          - url: "http://<LXC_IP>:3000"
    game-ws:
      loadBalancer:
        servers:
          - url: "http://<LXC_IP>:3001"

Set VITE_SERVER_URL to the public domain so Socket.io connects through the proxy:

# In client/.env.local
VITE_SERVER_URL=https://game.example.com

Security note: Do not expose vite dev to the internet. Use vite preview or serve client/dist/ with a static file server (nginx, serve, etc.) for production.

Summary of Ports

Service Port Protocol
Game server 3001 TCP
Client (dev) 3000 TCP
Client (prod) 80 TCP

Development Commands

Command Description
npm install Install all workspace dependencies
npm -w server run dev Start server with hot reload
npm -w server run start Start server (production)
npm -w server run test Run server tests
npm -w server run test:watch Run server tests in watch mode
npm -w client run dev Start client dev server
npm -w client run build Build client for production
S
Description
Multiplayer NPC simulation game (Dwarf Fortress-inspired)
Readme 5.4 MiB
Languages
TypeScript 99.9%