LegacyShell WikiLegacyShell Wiki
Back to LegacyShell
Home
Wiki
Plugins
Docs
Back to LegacyShell
Home
Wiki
Plugins
Docs
  • Documentation

    • Getting Started

      • What is LegacyShell?
      • Speed Setup
      • Requirements
      • Installation
      • First Run
      • Config Files
      • Making an Account
      • Troubleshooting (Getting Started)
    • Running a Server

      • Architecture Overview
      • The Database
      • Users and Ranks
      • Adding Game Servers
      • Client Mirrors
      • Perpetual
      • Backups
      • Rate Limiting
      • Moderation
      • Closed Mode
      • Deployment
      • Troubleshooting (Running a Server)
      • Hosting for Someone Else's Instance
    • Content Creation

      • Maps
      • Dealing with Babylon Models
      • Map Blocks
      • Items and Skins
      • Hats and Stamps
      • Sounds
      • Gamemodes
      • Seasonal Events
    • Plugin Development

      • Quickstart
      • I Want To...
      • Anatomy of a Plugin
      • Lifecycle
      • Dependencies
      • Events (Concept)
      • Event Reference

        • services: events
        • game: events — shared logic (src/shell/)
        • game: events — main-thread server process
        • game: events — per-connection client object
        • game: events — room lifecycle & tick loop
        • game: events — in-browser gameplay
        • client: events — client server & build pipeline
      • Commands
      • Client-Side Code
      • Static Assets
      • Content Packs
      • Networking
      • Workers and State
      • Prediction and Authority
      • Recipes

        • Recipe: Killstreaks
        • Recipe: New Pickup Item
        • Recipe: New Gamemode
        • Recipe: Custom Weapon
        • Recipe: UI Modification
        • Recipe: Discord Integration
        • Recipe: Replacing Core Behaviour
        • Recipe: Persistent Plugin Storage
        • Recipe: Rewarding Players with Currency
        • Recipe: Custom Per-Player Data
        • Recipe: Custom Theme
      • Publishing
      • Pitfalls
      • Modifiers
      • Sound and Apollo
    • Codebase Reference

      • Repo Layout
      • Shared Shell Layer
      • Server-Only Markers
      • The ss Object
      • Build Pipeline
      • Stamps and Babylons
      • Game Loop
      • Rooms and Workers
      • Wire Protocol
      • Generated

        • Wire Protocol Opcodes
        • Enums & Lookup Tables
        • Database Schema
        • Config Reference
        • Slash Command Reference
      • Services Internals
      • Catalog and Items
      • Permissions Internals
      • Physics and Collision
      • Known Quirks
      • Codebase Anecdotes
      • Development Timeline
    • Contributing

      • Documentation Style Guide
      • Generators
      • For AI Agents

Client Mirrors

Audience: Server operators · Prereqs: Architecture Overview

Canonical source: server-client/start-client.js

Unlike a game server (which needs authorization), a client server needs no registration at all - anyone can stand one up pointed at your services server, the same way anyone can run a mirror of a normal website. This page covers when and how to actually do that deliberately, as part of your own deployment.

Why you'd want more than one

  • Geographic spread - serving the (static, cacheable) game files from a location closer to your players reduces load time, independent of which game server region they end up playing on.
  • Redundancy - if one client server goes down, players hitting a different mirror are unaffected.
  • Load - the client server does real work on startup (building the browser bundle, generating the stamp spritesheet) and serves static assets afterward; spreading traffic across mirrors reduces load per machine, though a single client server can typically serve a lot of static traffic before this matters.

Setting one up

Nothing beyond a normal LegacyShell install (see Installation) plus pointing its config at your existing services server instead of a local one:

# store/config/client.yaml
port: 13370
sync_server: "ws://your-services-host:13371"   # or wss:// through a reverse proxy, see Deployment

Start it with npm run client as usual. It polls services for maps/items/servers/config the same way every client server does (see Architecture Overview) - there's no separate "register this mirror" step, because the client role is stateless and unauthenticated by design.

What's shared vs. independent between mirrors

  • Shared (pulled from services): maps, items, the server list, distributed config (distributed_all.yaml, distributed_client.yaml, distributed_permissions.yaml).
  • Independent (local to each mirror): client.yaml itself - port, closed mode, HTTP Basic Auth (login.enabled), this_url. Each mirror can have its own closed: true without affecting the others - see Closed Mode.

Common Issues

A mirror shows stale maps/items after I updated them on services. It hasn't re-polled yet, or the update didn't actually change what services reports as current - restart the mirror to force an immediate requestConfig, and confirm the change actually landed in services' database first (see The Database).

Mirrors need to serve different content from each other. That's not really what this mechanism is for - all mirrors serve the same game, synced from the same services server. If you need genuinely different content per audience, that's more of a plugin or separate-deployment question than a client-mirror one.

Next: Content Creation, or back to Adding Game Servers if you haven't set up your game-server side yet.


This page was drafted with AI assistance and reviewed for accuracy. If something looks wrong, please open a PR or flag it.

Edit this page on GitHub
Prev
Adding Game Servers
Next
Perpetual