Overview
An Ultima Online server written in C# on .NET 10, with reusable libraries for networking, persistence, scripting and internal APIs.
Install on Linux
Section titled “Install on Linux”curl -fsSL https://moongate.sh/install.sh | shInstalls the latest release into /opt/moongate and links it as moongate. See
Install on Linux for the options, upgrades and removal.
Getting started
Section titled “Getting started”Use First start to build and configure the server, or follow the Docker guide below. The configuration reference lists all TOML settings, CLI options and current implementation limits.
Docker
Section titled “Docker”Every release publishes a linux/amd64 image to
GitHub Container Registry.
See Run with Docker for first-start configuration, persistent
storage, Docker Compose, logs, and upgrades.
Server guides
Section titled “Server guides”- PostgreSQL persistence and world saves: Auth/World registration, async queries, transactions, schema operations and autosave.
- Packets and handlers: wire formats, default opcodes and typed game handlers.
- Game loop and timers: thread ownership, bounded queues, scheduling and shutdown.
- Writing Lua scripts: bootstrap, modules, timers, reload and editor support.
- Diagnostics: metrics and events; dependency security covers package auditing.
Status
Section titled “Status”Moongate is under active development. The transport, packet pipeline, scripting, persistence and internal API infrastructure are in place; account login, realm selection and a playable world are not. Implementation status lists what works today, area by area.
Scripting
Section titled “Scripting”Shard content runs in an embedded Lua 5.2 runtime (Moongate.Scripting, on
LuaCSharp) that lives entirely on the game
loop thread. Scripts sit under scripts/ in the server root; init.lua runs at
startup, and require resolves only inside that directory, symbolic links included.
-- scripts/init.lualog.info("booted {Engine} {Version}", engine.name, engine.version)
timer.every(30, function() log.info("tick") wait(2) -- parks this coroutine on the timer wheel log.info("two seconds later")end)- Modules:
engine(name, version, codename, platform),log(debug,info,warning,error, Serilog templates),timer(after,every,cancel) and the globalwait(seconds);printgoes to the server log. Host modules are C# classes marked[ScriptModule]/[ScriptFunction]/[ScriptConstant], registered withAddScriptModule<T>()inProgram.cs. - Budget: a deterministic instruction count, not a wall clock. A coroutine resume
may run 150,000 instructions and a top-level chunk 10,000,000 before it is aborted
with a script error;
string.reprefuses results longer than 16,777,216 characters. The budget bounds VM execution; C# bindings must avoid blocking, and total memory is not capped. - Sandbox: base,
string,table,math,coroutineandpackageonly; noio,os,debug,dofile,loadfile,rawsetor script-created coroutines. - Errors: every failure is logged with file and line and published as
ScriptErrorEventon the event bus; only aninit.luafailure refuses the start. - Tooling: at startup the engine writes
scripts/definitions.luaandscripts/.luarc.json, so an editor with the Lua language server completes every bound module, function, constant and enum. The console offersscript reload <file>andscript metrics.
[scripting]bootstrap_file = "init.lua"max_instructions_per_resume = 150000max_instructions_per_chunk = 10000000hook_interval = 1000write_definitions = truemax_string_length = 16777216Start with Writing Lua scripts. The package README documents the binding model and sandbox.
Extending Moongate
Section titled “Extending Moongate”- Writing a plugin: an assembly under
plugins/that registers services, commands, Lua modules and metric providers before the server starts. - Writing a Lua module: a C# class with
[ScriptModule]and[ScriptFunction]that scripts call as a read-only table. - Registering a metric provider: an
IMetricProviderwhose samples join the diagnostics snapshot. - Loading TOML templates: an
IDataLoader<TEntity>that reads shard content once at startup, plusEnumValueSpec<TEnum>for fields that resolve randomly.
The first three are shown by one compiled sample, samples/Moongate.Sample.Plugin, which the test suite loads through the real plugin loader.
Libraries
Section titled “Libraries”The eight library packages have their own English READMEs and runnable examples. See NuGet libraries and package verification for the package list, dependencies, and the local verification command.
Documentation
Section titled “Documentation”The documentation website includes the changelog, server guides, and library documentation. Releases publish the site automatically, and the same workflow can be run by hand between releases. See Writing documentation for local preview commands and how to contribute a page.
Contributing
Section titled “Contributing”Read CONTRIBUTING.md for development setup, coding conventions,
validation commands, and the pull request workflow. Contributions target develop.
