Royale is an experimental cross-platform multiplayer battle royale built from the ground up in C# on .NET 10.
The goal is not to reproduce the scale or feature set of a commercial battle royale. The project exists to prove that a small custom technology stack can support a complete server-authoritative multiplayer match loop across macOS, Linux, and eventually Windows.
Current status: early prototype. The client can open an SDL3 window, render a gray-box world through SDL GPU, display ImGui diagnostics, run local first-person movement, use Box3D-backed collision helpers, fire a default hitscan rifle at a training dummy, and render BlurgText-based screen/world labels. Dedicated server, networking, battle-royale match flow, loot, packaging, and full cross-platform verification are still in progress.
- Native macOS and Linux clients, with Windows planned later.
- A Linux dedicated server that runs headlessly without SDL windowing or GPU initialization.
- Server-authoritative movement validation, combat, health, ammunition, safe zone, match phases, eliminations, winner selection, and reset.
- Shared simulation and protocol libraries without weakening server authority.
- A focused game-specific renderer and platform layer, not a general-purpose engine.
- Incremental vertical slices that remain visible, testable, or playable.
- .NET 10 and C#
- SDL3 for windowing, input, events, and platform integration
- SDL GPU for rendering
- Box3D through focused C# bindings and managed wrappers
- ImGui through SDL3 and SDL GPU backends for development tooling
- BlurgText for game-facing text outside ImGui
- WattleScript planned for automated gameplay scenarios
src/
Royale.Client/ SDL3 client, rendering, input, diagnostics
Royale.Server/ Dedicated server entry point
Royale.Simulation/ Shared movement, combat, collision, gameplay logic
Royale.Protocol/ Protocol constants and future message definitions
Royale.Content/ Shared map and weapon content
Royale.Box3D.Bindings/ Low-level Box3D C API bindings
Royale.Box3D/ Managed Box3D wrappers
Royale.Native/ Native library resolution
Royale.Diagnostics/ Shared logging
tests/ xUnit tests by project area
thirdparty/ Pinned dependency fetch/build scripts and patches
.pm/ Public PM board and wiki source of truth
- .NET SDK
10.0.301, as pinned byglobal.json gitshadercrossonPATH- CMake for native Box3D and BlurgText builds
- macOS ARM64 for the currently complete client native build path
- SDL3 development headers visible to
pkg-configwhen building the ImGui SDL3/GPU shim on macOS
The native helper scripts currently focus on macOS ARM64, with some Linux build work present for Box3D. Linux and Windows client packaging are planned tasks, not completed release targets.
Third-party source is not committed as submodules. Pinned repositories are fetched into ignored directories under thirdparty/repos/, and project-specific changes belong in ordered patch files under thirdparty/patches/.
Fetch all pinned dependencies:
sh thirdparty/fetch-all.shBuild the macOS ARM64 native artifacts currently needed by the client:
sh thirdparty/build-box3d-macos.sh
sh thirdparty/build-imgui-macos.sh
sh thirdparty/build-blurgtext-macos.shSee thirdparty/README.md and the PM wiki pages under .pm/wiki/third-party-dependencies/ for dependency policy, pins, layout, and patch workflow.
Restore after fetching third-party source:
dotnet restore Royale.slnx -p:CI_DONT_TARGET_ANDROID=1Build:
dotnet build Royale.slnx -m:1 --no-restoreTest:
dotnet test Royale.slnx -m:1 --no-restoreVerify formatting for the editor sources and tests:
dotnet format Royale.slnx --no-restore --verify-no-changes --include src/Royale.Editor tests/Royale.Editor.TestsApply the formatting policy to that scope with the same command after removing --verify-no-changes.
The -m:1 --no-restore flags are intentional for Codex/sandboxed sessions and are also fine for local deterministic builds after restore.
Run the current client prototype:
dotnet run --project src/Royale.Client/Royale.Client.csproj -p:CI_DONT_TARGET_ANDROID=1 -- --config config/client.production.jsonThe development profile connects to the local dedicated server without repeating endpoint arguments:
dotnet run --project src/Royale.Client/Royale.Client.csproj -p:CI_DONT_TARGET_ANDROID=1 -- --config config/client.development.jsonUseful development controls:
F1toggles relative mouse capture.F2toggles gameplay view and freecam.F5renders normal world solids.F6renders world solids plus debug wireframes.F7renders debug wireframes only.F8renders collision solids.Escapereleases mouse capture before quitting.
The client also supports screenshot capture:
dotnet run --project src/Royale.Client/Royale.Client.csproj -p:CI_DONT_TARGET_ANDROID=1 -- --screenshot /tmp/royale-frame.png --screenshot-after-frames 5Deterministic validation captures can start directly in freecam. Camera vectors use invariant-culture x,y,z floats and are accepted only with --camera-mode freecam:
dotnet run --project src/Royale.Client/Royale.Client.csproj -p:CI_DONT_TARGET_ANDROID=1 -- --offline --map graybox --camera-mode freecam --camera-position 4,2.2,3 --camera-look-at 1.75,0.7,-1.35 --screenshot /tmp/royale-crate.png --screenshot-after-frames 5The server project runs a headless fixed-timestep simulation, loads the selected map, and builds the server-owned Box3D static collision world without SDL, SDL GPU, ImGui, or client rendering dependencies:
dotnet run --project src/Royale.Server/Royale.Server.csproj -- --port 7777 --map grayboxProduction and short-duration local settings are also available as explicit profiles:
dotnet run --project src/Royale.Server/Royale.Server.csproj -- --config config/server.production.json
dotnet run --project src/Royale.Server/Royale.Server.csproj -- --config config/server.development.jsonBoth executables merge built-in defaults, then the selected JSON profile, then explicit CLI arguments. --config <path> may appear anywhere once; relative paths resolve from the current working directory. Profiles allow comments and trailing commas, but reject unknown fields, malformed JSON, and invalid merged settings. There is no automatic profile discovery or environment-variable expansion.
The default lobby starts preparation when two humans connect or the five-minute wait expires, fills the roster to eight with server-owned bots, and starts play after two minutes of preparation. Override those values for local validation or deployment automation:
dotnet run --project src/Royale.Server/Royale.Server.csproj -- --minimum-players 2 --target-players 8 --waiting-seconds 5 --preparation-seconds 5For deterministic validation runs, provide a finite tick count:
dotnet run --project src/Royale.Server/Royale.Server.csproj -- --map graybox --run-ticks 5Real networking, player state, and match flow are still planned work. See the PM board for current task state.
For local OpenTelemetry collection, the Kustomize-managed development stack lives under deploy/observability/. See deploy/observability/local/README.md and the architecture/observability wiki page for overlay and port-forward instructions.
This repository keeps its PM board and wiki in .pm/.
Important wiki entry points:
.pm/wiki/project-overview.md.pm/wiki/architecture.md.pm/wiki/third-party-dependencies.md.pm/wiki/diagnostics.md
Agent workflow and repository rules are documented in AGENTS.md. PM storage should be changed through PM tools, not by manually editing .pm task or wiki files.
This project is licensed under the MIT License. See LICENSE.
Third-party dependencies retain their own licenses and notices. Distribution or packaging work must carry the required upstream notices for bundled native and managed dependencies.
