Highly Performant & Scalable Ultima Online Server Emulator
https://www.modernuo.com
c-sharpcsharpcsharp-coredotnetdotnet-coreemulatorgplgplv3mmommo-enginemmorpgmmorpg-serveronlinerpgrunuoserverservuoultimaultima-onlineuo
## Summary Adds a binary disk format + lazy reader so the step cache can warm-start from a precomputed file without paying chunk-build cost on the first pathfind through a region. **Resident memory stays bounded by `MaxResidentChunks` regardless of file size** — opening a `.swb` reads only the header + chunk-offset index (~16 bytes per indexed chunk), and individual chunks are seeked + deserialized only when `ResolveMissingChunk` asks for them. The lazy design (vs. an eager bulk load): a 250 MB bake on a RAM-constrained shard never materializes more than the LRU cap (~40 MB at the default 8192-chunk cap), and unwanted regions never enter memory at all. Builds on PR #2447. ## What changed - **`StepCacheFile`** — binary reader/writer module. Writer emits header → chunks (offsets recorded) → index trailer, then patches the header's `IndexOffset` field. Reader is `OpenForLazy(path)` returning a `LazyReader` that holds an open `FileStream` + offset dictionary. - **`StepCacheFile.LazyReader`** — `TryReadChunk(chunkX, chunkY)` does a single seek + bulk read for one record. `Dispose` releases the underlying stream. Files are opened with `FileShare.Read | FileShare.Delete` so admin tooling can replace them. - **TileData fingerprint via XxHash3.** The `.swb` header carries a hash of `LandTable + ItemTable` flags. Load rejects any file whose hash doesn't match the running server. Computed via `HashUtility.ComputeHash64` (engine-blessed hasher) — adds a `ReadOnlySpan<byte>` overload alongside the existing `ReadOnlySpan<char>` one for parity. - **`StepCache.SaveToFile(path, mapId)`** — writes resident chunks for the given map. - **`StepCache.TryOpenLazyReader(path, mapId)`** — opens the file, validates header, holds the reader for the map's lifetime. - **`StepCache.ResolveMissingChunk`** — now consults the lazy reader before invoking the runtime baker. A loaded chunk whose `BuiltMultisVersion` doesn't match the live sector falls through to the baker (snapshot was made before a multi was added/removed in that sector). - **`StepCache.Clear` closes lazy readers.** Test cleanup can delete `.swb` files cleanly. - **Auto-load at startup.** `PathCacheCommands.Configure()` opens `Data/Pathfinding/<mapId>.swb` as a lazy reader for every map. - **`[PathCacheSave`** / **`[PathCacheLoad`** — admin commands for the same workflow. - **`pathfinding.maxResidentChunks` shard-tunable.** Read from `server.cfg` at boot via `ServerConfiguration.GetOrUpdateSetting` (default 8192 ≈ 40 MB). Small shards can tune down; large shards with substantial bakes can tune up to reduce eviction churn. Default is written back to `server.cfg` on first boot, matching the engine pattern used by other settings. ## File layout (v1) ``` Header (48 bytes): u32 Magic = 0x42575300 ('SWB\0') u32 Version = 1 u32 MapId u64 TileDataHash XxHash3 over LandTable + ItemTable flags (HashUtility) u64 BakeTimestamp informational u32 ChunkCount u64 IndexOffset file position where the chunk index begins Chunk records (fixed size, ~5,393 bytes each, +32 if multi-Z): u16 ChunkX u16 ChunkY u32 BuiltMultisVersion u8 HasMultiZ byte WalkMask[256], WetMask[256] sbyte SourceZ[256], WalkZN..WalkZNW[256], SwimZN..SwimZNW[256] [byte MultiZCells[32] when HasMultiZ == 1] Index trailer (16 × ChunkCount bytes): (u64 chunkKey, u64 fileOffset) ``` ## Memory math | Scenario | Disk file | RAM at boot | Notes | |---|---|---|---| | Empty / no `.swb` files | — | 0 | Silent; cache builds on demand. | | Admin-curated towns (5K chunks) | 25 MB | 0 + per-query | Index ≈ 80 KB. Resident grows to the configured cap under steady-state queries. | | Full-map bake (50K chunks) | 250 MB | 0 + per-query | Index ≈ 800 KB. Same configured cap. Cold areas never load. | | All 5 maps fully baked | 1.25 GB | 0 + per-query | Index ≈ 4 MB total. Same configured cap. | ## Hash choice (FNV-1a → XxHash3) The original draft used inlined FNV-1a-64. Switched to XxHash3 via `HashUtility`: - ~30× faster on this workload (~30 GB/s SIMD vs ~2 GB/s byte-by-byte). Boot-time only, so absolute saving is microseconds — the real wins are elsewhere. - Stronger collision resistance and distribution. - Drops ~25 lines of inlined hash code; matches the rest of the codebase's hashing pattern. - Hash is stable as long as `HashUtility`'s `xxHash3Seed` constant doesn't change (already marked `// DO NOT CHANGE THIS NUMBER`). |
||
|---|---|---|
| .config | ||
| .github | ||
| dev-docs | ||
| Distribution/Data | ||
| Projects | ||
| .cursorrules | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| AGENTS.md | ||
| azure-pipelines.yml | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| Directory.Build.props | ||
| FAQ.md | ||
| GEMINI.md | ||
| global.json | ||
| LICENSE | ||
| ModernUO.slnx | ||
| publish.cmd | ||
| publish.ps1 | ||
| publish.sh | ||
| README.md | ||
| rider-settings.zip | ||
| Rules.ruleset | ||
| SPONSORS.md | ||
| stylecop.json | ||
| THIRD-PARTY-NOTICES | ||
| version.json | ||
ModernUO

Ultima Online Server Emulator for the modern era!
Requirements
Supported Operating Systems
Required Frameworks
All Operating Systems
Windows
Development
Supported IDEs
Getting Started
- Install prerequisite requirements
- Clone this repository (or download the latest):
git clone https://github.com/modernuo/ModernUO.git
- Open
ModernUO.slnto start developing
Building/Publishing
Interactive Mode (Recommended for new users)
Run ./publish.cmd (Windows) or ./publish.sh (Linux/macOS) with no arguments to launch the guided build tool. It will:
- Check prerequisites (.NET SDK, native libraries)
- Walk you through configuration and platform selection
- Build and publish the server to the
Distributiondirectory - Show deployment instructions for cross-compiled builds
Command Line
./publish.cmd [release|debug] [os] [arch]
os- Supported operating systemsarchx64- Intel/AMD 64-bitarm64- ARM 64-bit
Linux Prerequisites
Fedora, CentOS, RHEL, etc
dnf upgrade --refresh -y
# CentOS does not come with EPEL enabled
dnf install -y epel-release epel-next-release
dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel liburing-devel
Ubuntu, Debian, etc
apt-get update -y
apt-get install -y libicu-dev libdeflate-dev zstd libargon2-dev liburing-dev
OSX Requirements
brew install icu4c libdeflate zstd argon2
Running the Server
- Follow the publish instructions
- The
Distributiondirectory is portable — copy it to your production server for deployment - Run
ModernUO.exeordotnet ModernUO.dllfrom theDistributiondirectory - On first run, the server will prompt you to configure game data file locations
Troubleshooting / FAQ
- See FAQ
Want to sponsor?
Thank you for supporting us! You can find out how by visiting the sponsors page.
Collaborators
Thanks
- RunUO Team & Community
- Voxpire, the ServUO Team & Community
- Karasho, Jaedan and the ClassicUO Community