Files
max bittkerandClaude Opus 5.5 ca34325e7a feat(gateway,sdk): compressed direct gateway listener; private fleet gateways
The gateway can also listen on GATEWAY_PUBLIC_PORT (on Fly:
wss://rs-sdk-demo.fly.dev:7443 -> 7781) with permessage-deflate. On live
bot state frames that is ~16% of the uncompressed bytes at ~6 ms CPU per MB,
spent in the gateway process rather than on the game server's tick thread.
The main port is unchanged and uncompressed, so the engine's /gateway relay
and every existing SDK and runner behave exactly as before.

SDK connect() and the lite runner check the direct port (3 s) and use it when
it answers, otherwise they keep wss://.../gateway; a failed connect checks
again. GATEWAY_DIRECT=false opts out. HTTP endpoints keep the relayed origin.

GATEWAY_HOSTNAME lets a fleet run a private loopback gateway (sdk/README.md).
sdk/chat.ts and the MCP server now honor a bot.env GATEWAY_URL, as cli.ts and
runScript already did. The Dockerfile sed that forced 0.0.0.0 is replaced by
that option's default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:46:40 -04:00

119 lines
4.0 KiB
Docker

# syntax=docker/dockerfile:1
FROM oven/bun:debian
# Install system deps with cache mount
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt install -y --no-install-recommends \
git ca-certificates bash sqlite3
WORKDIR /opt/server
# === DEPENDENCY LAYER (cached unless package files change) ===
# Copy only package files first
COPY server/engine/package.json server/engine/bun.lock /opt/server/engine/
COPY server/gateway/package.json server/gateway/bun.lock /opt/server/gateway/
COPY server/webclient/package.json server/webclient/bun.lock /opt/server/webclient/
# Install engine deps with bun cache mount
WORKDIR /opt/server/engine
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install
# Install gateway deps with bun cache mount
WORKDIR /opt/server/gateway
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install
# Install webclient deps with bun cache mount
WORKDIR /opt/server/webclient
RUN --mount=type=cache,target=/root/.bun/install/cache \
bun install
# === SOURCE LAYER (rebuilds on code changes, but deps cached) ===
WORKDIR /opt/server
# Copy source code
COPY server/content /opt/server/content
COPY server/webclient /opt/server/webclient
COPY server/engine /opt/server/engine
COPY server/gateway /opt/server/gateway
COPY sdk /opt/sdk
# Build webclient bundle (out/standard, out/bot, out/viewer). out/ is gitignored and
# NOT shipped from the local build context (see .dockerignore), so it must be built
# here — otherwise the engine would serve a stale or missing client. The engine serves
# this from ../webclient/out relative to its CWD (i.e. /opt/server/webclient/out).
WORKDIR /opt/server/webclient
RUN bun run build
# Patch and build engine
# 274 removed .env.example; runtime config now comes from process.env (overlaid in
# Environment.ts) / data/config/world.json, so no .env file is needed at build time.
WORKDIR /opt/server/engine
RUN sed -i 's/port: Environment.WEB_PORT,/port: Environment.WEB_PORT, hostname: "0.0.0.0",/' src/web/index.ts && \
BUILD_VERIFY=false bun run build
WORKDIR /opt/server
EXPOSE 8080/tcp
EXPOSE 43594/tcp
EXPOSE 7780/tcp
# Entrypoint script to ensure persistent data is on the volume
COPY --chmod=755 <<'EOF' /opt/server/entrypoint.sh
#!/bin/bash
set -e
# === DATABASE ===
# Symlink db.sqlite to persistent volume
if [ ! -L /opt/server/engine/db.sqlite ]; then
rm -f /opt/server/engine/db.sqlite
ln -s /opt/server/data/db.sqlite /opt/server/engine/db.sqlite
fi
# Run migrations (idempotent - only applies pending migrations)
echo "Running database migrations..."
cd /opt/server/engine && bun run sqlite:migrate
# === PLAYER SAVES ===
# Create players directory on volume if it doesn't exist
mkdir -p /opt/server/data/players
# If there are existing player saves in ephemeral storage, move them to volume
if [ -d /opt/server/engine/data/players ] && [ ! -L /opt/server/engine/data/players ]; then
# Copy any existing saves to volume (won't overwrite existing)
cp -rn /opt/server/engine/data/players/* /opt/server/data/players/ 2>/dev/null || true
rm -rf /opt/server/engine/data/players
fi
# Create symlink for player saves
mkdir -p /opt/server/engine/data
if [ ! -L /opt/server/engine/data/players ]; then
rm -rf /opt/server/engine/data/players
ln -s /opt/server/data/players /opt/server/engine/data/players
fi
# === START GATEWAY SERVICE ===
# Supervised: the engine runs as PID 1, so if the gateway dies the container stays
# "healthy" while SDK connectivity is silently dead until a redeploy. Respawn it
# instead, with a short backoff so a crash-on-boot can't spin the CPU.
echo "Starting gateway service on port 7780..."
(
cd /opt/server/gateway
while true; do
bun run gateway.ts
echo "[entrypoint] Gateway exited (code $?), restarting in 2s..." >&2
sleep 2
done
) &
# === START GAME SERVER ===
cd /opt/server/engine
exec bun run src/app.ts
EOF
CMD ["/opt/server/entrypoint.sh"]