all posts

MCGame — A self-hosted Go server for a legacy Flash MMO client

MCGame is a server for running the backend of a Flash MMO client on your own machine. It is written in Go 1.23 and deployed as a Docker Compose stack. It speaks the same wire protocol as the original service, so an unmodified Flash client connects to it without patches.

The project exists for software preservation and interoperability: when the original service goes offline, the client should still be able to run. Source code is at github.com/luthebao/mcgame.

Please note. MCGame is an independent, unofficial project. It is not affiliated with the original game’s developers or publishers. Read the repository’s DISCLAIMER before using, copying, or distributing anything in it.

Overview

Concern Implementation
Language Go 1.23
Transport RTMPE v6 (HMAC-SHA256, Diffie-Hellman, RC4) with AMF0 payloads
Persistence Supabase Postgres
Cache Redis
Administration Web dashboard (Next.js)
Deployment Docker Compose; monolith by default, or main + line servers over gRPC

Quick start

Requirements: Docker with the Compose v2 plugin, GNU Make (macOS and Linux), and the Supabase CLI for applying migrations. Go 1.23 is only needed if you build the server on the host.

git clone https://github.com/luthebao/mcgame.git
cd mcgame

make game setup   # one-time: generates docker/.env with fresh secrets
make game up      # database, migrations (and seeds on a fresh database), then every service

The first up builds the images and takes a few minutes. Day-to-day operations:

make game down    # stop all services; data is preserved
make game reset   # restore the database and storage to the migrations and seeds, then start

On Windows, the bundled Go tool replaces make and has no dependency on sh, openssl, or Node:

go build -o bin/game.exe ./cmd/game
bin\game setup
bin\game up

The following ports must be free: 8000 (Supabase API gateway), 54322 (Postgres, bound to 127.0.0.1), 54323 (Studio, bound to 127.0.0.1), 6379 (Redis), 1935 (RTMP edge), 8888 (nginx asset edge), 3000 (admin dashboard), and 2112 (metrics).

Connecting a client

The client is a Flash SWF, so it needs a Flash runtime. The repository includes the standalone Flash Player 32 debug projector for Windows and macOS. Open it, choose File → Open…, and enter:

http://localhost:8888/s/sv/GameLoaders.swf?isExpand=true

Log in with an account from the seeded public.accounts table, select a line, and create or choose a character. The client locates the server through frontend/s/sv/profile/config.xml; changing its <resource> and <logic> values points the client at a different host.

Architecture

MCGame follows a four-layer clean architecture. Dependencies point inward, and the wiring happens in one place:

cmd/gameserver/main.go                  # dependency wiring
  └─ internal/presentation/             # RTMP and gRPC handlers
       └─ internal/application/         # use-case services
            └─ internal/domain/         # entities and repository interfaces
                 └─ internal/infrastructure/   # Postgres, Redis, RTMP, gRPC

A request travels from the Flash client through RTMPE and the RTMP server connection, into a command dispatcher, then through a handler, an application service, and a repository down to Postgres. Handlers register against the dispatcher by RPC name, which keeps each feature self-contained.

Data lives in a single Postgres instance split into three schemas: public for authentication, player for runtime state, and data for static game templates.

Wire compatibility

Because the client is unmodified, the server has to match it exactly. That drives a few conventions throughout the codebase:

  • RPC method names mirror the client verbatim, including its original misspellings.
  • AMF0 numbers arrive as float64, so handlers type-switch on arguments instead of assuming integers.
  • DTO field names follow what the client expects, and identifier-like fields such as icon and resource codes are serialised as int64.
  • Callback argument lists must match the client’s ActionScript signatures precisely; the decompiled client is kept in the repository as a read-only reference.

Scaling topology

By default everything runs in one process. For larger deployments the same binary can be split into a main server (authentication and line registry) and one or more line servers (gameplay) that communicate over gRPC.

Admin dashboard

A Next.js dashboard ships with the stack on port 3000. It covers day-to-day operation of a server without touching SQL: player management and item delivery, items and creatures, boss and general loot tables, daily sign-in rewards, gift-code campaigns, events, line management, game configuration, and a map viewer.

Extending the server

Adding a feature follows a fixed path, one step per layer:

  1. Define the domain entity and repository interface under internal/domain/<feature>/.
  2. Implement the Postgres repository under internal/infrastructure/persistence/postgres/.
  3. Add the use-case service under internal/application/<feature>/.
  4. Add the handler under internal/presentation/rtmp/handlers/<feature>/ with the signature func(ctx *rtmp.RPCContext, args []interface{}) (interface{}, error).
  5. Register it through RegisterHandlers(dispatcher) and wire it from cmd/gameserver/main.go.

Configuration is environment-only: defaults live in code and are overridden by MCGAME_* variables, which Compose populates from docker/.env. The test suite runs with make test, and make test-race enables the race detector.

Security and operations

The default configuration targets local development, and the project has not been through a formal security audit. Before exposing an instance beyond your own machine:

  • Rotate every secret in docker/.env, including the dashboard and Studio credentials.
  • Keep Postgres and Studio bound to 127.0.0.1.
  • Treat player data as your responsibility, including compliance with the privacy laws that apply to you.

Licensing and scope

The code written for this project is licensed under Apache 2.0. Third-party material keeps its own terms and is not covered by that license: the vendored RTMP and AMF0 forks (Boost Software License 1.0), the Supabase-derived Docker files, and the game client files, assets, and Flash Player projector. You are responsible for ensuring you have the right to use those materials in your jurisdiction. Rights holders who want something removed can open an issue on the repository.

Status

MCGame is under active development. The database schema, migrations, and internal packages may change without notice, and there is no stability guarantee between commits. Contributions are welcome — open an issue to discuss a change first, and see the repository’s contributing section for the conventions above.