Deterministic Multiplayer Simulation Framework for Unity
⚠️ Experimental Release This project is under active development and has not yet reached a stable stage. Public APIs, serialization formats, and network protocols may change without notice. Production use is not recommended.
A Unity-based framework supporting Client-Side Prediction (CSP), Rollback, Frame Synchronization, Server-Driven mode, and Replay. By excluding floating-point and building the simulation solely on 32.32 fixed-point (FP64) and a deterministic RNG (Xorshift128+), it guarantees full reproducibility across platforms and compilers.
Klotho weaves the simulation, one frame at a time.
| Area | Contents |
|---|---|
| Network Models | P2P Lockstep · Rollback + Client Prediction · Server-Driven (Authoritative) · Dedicated Server · Spectator · Late Join · Reconnect |
| Deterministic Math | FP64 (32.32 fixed-point) · FPVector2/3/4 · FPQuaternion · FPMatrix · LUT/CORDIC-based trigonometry · DeterministicRandom (Xorshift128+) |
| Physics | FPPhysicsWorld · Broadphase (SpatialGrid) · Narrowphase · CCD (Sweep) · Constraint Solver · Joints · Triggers · Static BVH |
| Navigation | FPNavMesh · A* (triangle graph) · Funnel (SSFA) · ORCA avoidance · ECS-integrated NavAgentComponent |
| ECS | Sparse-set ComponentStorage<T> · Frame (single byte[] heap) · FilterWithout/Filter<T1..T5> · FrameRingBuffer · SystemRunner |
| Serialization / Source Generator | SpanWriter/Reader (ref struct, GC-free) · automatic code generation via [KlothoComponent] / [KlothoSerializable] / [KlothoDataAsset] |
| Data Assets | IDataAsset · DataAssetRegistry · DataAssetRef · JSON serialization (xpTURN.Klotho.DataAsset.Json) |
| Replay | Record / playback / seek / variable speed · LZ4 compression (K4os.Compression.LZ4) |
| Verification Tools | SyncTestRunner (GGPO-style determinism verification) · DeterminismVerificationRunner · benchmark suite |
| Unity Integration | USimulationConfig · USessionConfig · View layer (EntityViewFactory / EntityViewUpdater / EntityView, BindBehaviour / ViewFlags, VerifiedFrameInterpolator) |
Genres that benefit most from Klotho's deterministic simulation features.
- Fighting games — frame-perfect inputs + rollback netcode
- Platform fighters / arena brawlers
- 2–4 player PvP action — optimal for small-roster P2P rollback
- Tactics / turn-based SRPG — determinism + replay + sync verification
- Real-time strategy (RTS) — lockstep frame sync
- MOBA / top-down arena — ECS, physics, and navigation all included
- Twin-stick shooters / co-op shooters — deterministic physics + ORCA avoidance
- Auto-battlers — deterministic simulation + shareable replays
- Roguelike / roguelite (PvE co-op) — deterministic RNG (Xorshift128+) for shared seeds and replays
- Card / board / deckbuilder PvP — low input volume, strong verification and replay
- Puzzle PvP / falling-block versus
- Racing (small scale) — fixed-point physics guarantees determinism for small grids
- Top-down survival action
┌─────────────────────────────────────────────────────────────┐
│ Game Application │
│ (ISimulation impl: EcsSimulation or custom Simulation) │
└───────────────────────────┬─────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌──────────────┐ ┌────────────────────┐
│ KlothoEngine │ │ ReplaySystem │ │KlothoNetworkService│
│ (orchestrator)│◄ │ (record/play)│ │ │
└──────┬────────┘ └──────────────┘ └─────────┬──────────┘
│ │
┌────┴────┐ ┌─────┴──────┐
▼ ▼ ▼ ▼
ISimulation InputBuffer INetworkTransport NetworkMessages
│
├── (ECS) Frame — EntityManager + ComponentStorage[]
├── SystemRunner — ISystem[]
├── FrameRingBuffer (snapshots / rollback)
└── DataAssetRegistry
Three-layer separation:
- Klotho engine layer — Pure C#, Unity-independent. The same binary also runs on the server side (.NET console / ASP.NET).
- Simulation transport layer — UDP transport over the
INetworkTransportabstraction (Input / InputAck / SyncCheck / Handshake). LiteNetLib is provided as the default reference implementation; replaceable with any other library. - Game service layer — Lobby / matchmaking / authentication (external gRPC, etc.; integrated outside this project).
- Unity 2022.3+
- C# 8.0 (some assemblies opt into newer C# language features via the
xpTURN.Polyfillpackage, which supplies the runtime-attribute shims required by C# 11) - UniTask — async (Cysharp)
- xpTURN.Klotho.Logging (IKLogger) — in-house structured logging (no external logging dependency). Optional MEL interop via the
Plugins~/Logging.Melsample adapter (Microsoft.Extensions.Logging.AbstractionsDLL is consumer-provided). - LiteNetLib — default reference implementation for UDP transport (MIT, pure C#, vendored under
Runtime/ThirdParty/LiteNetLib.v2.1.4). The network transport layer is abstracted via theINetworkTransportinterface and can be replaced with any other library. - Newtonsoft.Json — DataAsset JSON serialization (
com.unity.nuget.newtonsoft-json) - K4os.Compression.LZ4 — replay compression (vendored under
Runtime/ThirdParty/)
Details: Docs/BaseLibraries.md
- Open Window > Package Manager
- Click + > Add package from git URL...
- Enter each URL in order (the first two are required git dependencies that UPM cannot auto-resolve):
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
https://github.com/xpTURN/Polyfill.git?path=src/Polyfill/Assets/Polyfill
https://github.com/xpTURN/Klotho.git?path=Klotho/Packages/com.xpturn.klotho
Pin a specific Klotho version with #vX.Y.Z (e.g. https://github.com/xpTURN/Klotho.git?path=Klotho/Packages/com.xpturn.klotho#v0.2.6).
Unity registry packages (com.unity.inputsystem, com.unity.ai.navigation for the NavMesh exporter, com.unity.nuget.newtonsoft-json) resolve automatically via the package's dependencies field.
Klotho uses C# 11 features (required, init, custom interpolated string handlers, etc.) in some assemblies. After installing xpTURN.Polyfill, enable the language version once per project:
- Run Edit > Polyfill > Player Settings > Apply Additional Compiler Arguments -langversion (All Installed Platforms).
- Settings are persisted to ProjectSettings/xpTURN.Polyfill.Settings.json.
This adds -langversion:preview to Player Settings (Unity build), inserts <LangVersion>preview</LangVersion> into regenerated .csproj (IDE), and defines the CSHARP_PREVIEW scripting symbol. Without this step, Klotho assemblies that rely on C# 11 syntax may fail to compile. Details: Polyfill README.
After install, open Unity Package Manager → select xpTURN.Klotho → Samples → "Import" to copy the MEL Logging Plugin adapter into your Assets/Samples/. Activating the adapter still requires you to supply Microsoft.Extensions.Logging.Abstractions (consumer-provided).
P2pSample — a minimum P2P sample (2 cubes, 60s sumo match) consuming this package via the same UPM git URL. Located at Samples/P2pSample/ in this repo — open the folder directly in Unity Hub. See Samples/P2pSample/README.md for the 4-step quick start, or Docs/Samples/P2pSample.md for the architecture walkthrough.
SdSample — the ServerDriven sibling of P2pSample (same sumo game, dedicated-server topology): a Unity client + a minimal .NET 8 dedicated server. Located at Samples/SdSample/ — see Samples/SdSample/README.md (server + 2 clients quick start) or Docs/Samples/SdSample.md (architecture + SD-specific gotchas).
Heavier demos (Brawler, NavMesh) are not bundled in the package — clone this repo if you want to inspect or modify them.
Klotho ships as Unity package source (not a binary NuGet), so a dedicated server builds the engine-agnostic assemblies from your vendored copy of the package. Server~/ holds per-assembly server projects that mirror the client asmdef structure; your server csproj <ProjectReference>s them. Two install patterns:
A. git submodule (recommended) — vendor this repo into your game project as a submodule under <yourGame>/Packages/com.xpturn.klotho, then reference the Server~ projects at the correct relative depth:
<!-- Server csproj at <yourGame>/Tools/MyDedicatedServer/MyDedicatedServer.csproj (nested 2 levels) -->
<ItemGroup>
<ProjectReference Include="..\..\Packages\com.xpturn.klotho\Server~\KlothoServer\KlothoServer.csproj" />
<ProjectReference Include="..\..\Packages\com.xpturn.klotho\Server~\xpTURN.Klotho.Runtime\xpTURN.Klotho.Runtime.csproj" />
<ProjectReference Include="..\..\Packages\com.xpturn.klotho\Server~\xpTURN.Klotho.Logging\xpTURN.Klotho.Logging.csproj" />
<ProjectReference Include="..\..\Packages\com.xpturn.klotho\Server~\xpTURN.Klotho.Gameplay\xpTURN.Klotho.Gameplay.csproj" />
<ProjectReference Include="..\..\Packages\com.xpturn.klotho\Server~\xpTURN.Klotho.LiteNetLib\xpTURN.Klotho.LiteNetLib.csproj" />
</ItemGroup>
<!-- Source generator for your game's [KlothoSerializable]/[KlothoComponent] types compiled into the exe -->
<ItemGroup>
<Analyzer Include="..\..\Packages\com.xpturn.klotho\Plugins\Analyzers\KlothoGenerator.dll" />
</ItemGroup>Adjust the ..\ depth to match where your csproj sits. At startup call KlothoServerBootstrap.Initialize("YourGamePrefix") — it force-loads the split assemblies and runs warmups so the cross-assembly [ModuleInitializer] registrations (commands / messages / components) complete before the first room is built.
B. UPM Library/PackageCache + <KlothoServerRoot> — if you don't want a submodule, point a property at your resolved PackageCache Server~ path. The @<hash> suffix changes on every pull, so this needs occasional refresh:
<PropertyGroup>
<KlothoServerRoot>$(MSBuildProjectDirectory)\..\..\Library\PackageCache\com.xpturn.klotho@1a2b3c4d\Server~</KlothoServerRoot>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(KlothoServerRoot)\KlothoServer\KlothoServer.csproj" />
<ProjectReference Include="$(KlothoServerRoot)\xpTURN.Klotho.Runtime\xpTURN.Klotho.Runtime.csproj" />
<!-- + Logging / Gameplay / LiteNetLib as above -->
</ItemGroup>Full guide (with Program.cs, callbacks, config files, single-room/multi-room/test CLI): Docs/Samples/Brawler.H.DedicatedServer.md — a Brawler-specific reference you can copy and adapt to your game.
Klotho ships as an embedded Unity Package (com.xpturn.klotho) located at Klotho/Packages/com.xpturn.klotho/. The dev project under Klotho/ is the source-of-truth host; consumers install via UPM (see Installation).
Klotho/ ← Unity dev project (this repo)
├── Packages/com.xpturn.klotho/ ← ★ framework package (UPM)
│ ├── package.json
│ ├── Runtime/
│ │ ├── Core/ KlothoEngine · KlothoSession · ISimulationCallbacks · IViewCallbacks
│ │ │ · ISimulationConfig · ISessionConfig · Command/Event/Pool families
│ │ ├── Logging/ xpTURN.Klotho.Logging (IKLogger — in-house structured logging)
│ │ ├── Gameplay/ built-in component / system reference implementations
│ │ ├── Diagnostics/ FaultInjection · RttSpikeMetricsCollector
│ │ ├── Input/ InputBuffer · SimpleInputPredictor
│ │ ├── Network/ IKlothoNetworkService · ServerDriven · ServerNetwork · Spectator
│ │ │ · Reconnect/LateJoin · Messages
│ │ ├── State/ RingSnapshotManager
│ │ ├── Serialization/ SpanWriter/Reader · SerializationBuffer
│ │ ├── Replay/ IReplaySystem · ReplayRecorder · ReplayPlayer · LZ4 compression
│ │ ├── ECS/ Frame · EntityManager · ComponentStorage<T> · SystemRunner
│ │ │ · DataAsset/ (IDataAsset · Registry · Json)
│ │ ├── Deterministic/ FP64 · FPVector* · Physics · Navigation · Random
│ │ ├── Unity/ USimulationConfig · USessionConfig · View/ · UnityDebugSink
│ │ ├── LiteNetLib/ LiteNetLibTransport (INetworkTransport implementation)
│ │ └── ThirdParty/ vendored: LiteNetLib.v2.1.4, K4os.Compression.LZ4.v1.3.8, ...
│ ├── Editor/ NavMesh · Physics · ECS · FSM · DataAsset tooling
│ ├── Plugins/Analyzers/ KlothoGenerator.dll (Roslyn source generator, RoslynAnalyzer label)
│ ├── Prefabs/ debug/visualization prefabs (EcsDebugBridge · FPPhysics*Visualizer)
│ ├── Plugins~/Logging.Mel/ opt-in MEL interop adapter (UPM "Import Sample")
│ └── Server~/ dedicated-server build assets (per-assembly csproj mirroring client asmdefs + KlothoServerBootstrap + Config helpers)
│
├── Assets/ ← dev-only (not redistributed via UPM)
│ ├── Brawler/ 4-player fighting-game sample
│ ├── NavMesh/ navmesh sample
│ ├── Tests/ unit / integration / determinism-verification tests
│ ├── Benchmarks/ performance benchmarks
│ └── Scenes/ Settings/ StreamingAssets/ ...
│
└── Tools/ ← .NET tooling (not redistributed)
├── KlothoGenerator/ Roslyn source generator (`IIncrementalGenerator`) — built by gen.build.sh
├── BrawlerDedicatedServer/ Brawler dedicated server (.NET console)
├── DeterminismVerification/ determinism verification (.NET console)
├── Generated/ reference copies of generated `.g.cs` (not included in Unity builds)
└── gen.build.sh generator build script
[KlothoComponent(100)] // 1–99: framework, 100+: game
public partial struct HeroComponent : IComponent
{
public int Level;
public int Experience;
}public class HeroSystem : ISystem
{
public void Update(ref Frame frame)
{
var filter = frame.Filter<HeroComponent, HealthComponent>();
while (filter.Next(out var entity))
{
ref var hero = ref frame.Get<HeroComponent>(entity);
// hero logic
}
}
}public class MySimulationCallbacks : ISimulationCallbacks
{
public void RegisterSystems(EcsSimulation sim)
{
/* AddSystem registrations */
}
public void OnInitializeWorld(IKlothoEngine engine)
{
/* spawn initial world */
}
public void OnPollInput(int playerId, int tick, ICommandSender sender)
{
/* send commands */
}
}
public class MyViewCallbacks : IViewCallbacks
{
public void OnGameStart(IKlothoEngine engine) { }
public void OnTickExecuted(int tick) { }
public void OnLateJoinActivated(IKlothoEngine engine) { }
}[SerializeField] private KlothoSessionDriver _sessionDriver;
private KlothoSession _session;
private KlothoSessionFlow _flow;
void Awake()
{
// Driver owns the Update / Stop lifecycle — no manual dt computation needed.
_sessionDriver.PreSessionUpdate += (s, dt) => _input.CaptureInput();
_sessionDriver.IdlePoll += () => transport.PollEvents();
}
void Start()
{
_flow = new KlothoSessionFlow(new KlothoFlowSetup
{
Logger = logger,
Transport = transport,
AssetRegistry = dataAssetRegistry,
LifecycleObserver = this,
CallbacksFactory = (simCfg, sessionCfg) =>
new SessionCallbacks(new MySimulationCallbacks(), new MyViewCallbacks()),
});
_flow.OnSessionCreated += s => _sessionDriver.Attach(s);
_session = _flow.StartHost(uSimulationConfig, uSessionConfig);
_session.HostGame("MyRoom", maxPlayers: 2);
}KlothoSessionFlow exposes 6 entry points — pick one per game mode:
StartHost(simCfg, sessionCfg)— P2P host (synchronous).JoinP2PAsync(transport, host, port, sessionCfg, ct)— P2P guest join.JoinServerDrivenAsync(transport, host, port, roomId, sessionCfg, ct)— ServerDriven client join (with roomId).ReconnectAsync(transport, creds, sessionConfigSeed, ct)— cold-start reconnect;credsis aPersistedReconnectCredentials(carriesRoomId, host address, and the magic token). Mode is recovered from the credentials.SpectateAsync(host, port, roomId, ct)— spectator entry (transport is instantiated byKlothoFlowSetup.SpectatorTransportFactory).StartReplayFromFile(path)— file-to-session replay (throwsReplayLoadExceptionon load failure).
If your game-side code needs to branch on the active mode, use KlothoModeStrategy.Resolve(simCfg) rather than inspecting simCfg.Mode directly. Per-mode session-created callbacks are also available (OnHostSessionCreated / OnGuestSessionCreated / OnReplaySessionCreated / OnSpectatorSessionCreated) alongside the generic OnSessionCreated. Stop teardown runs through the driver's Stopping hook; game code can read KlothoSessionDriver.IsStopping to short-circuit re-entrant teardown calls.
Detailed guides: Docs/GameDevWorkflow.md, Docs/GameDevAPI.md
Klotho/Assets/Brawler — a 4-player fighting-game sample (in the dev project of this repo)
- ECS-based combat / movement / skills / cooldowns / knockback / items / traps
- HFSM-based bot AI (
BotHFSMRoot/BotActions/BotDecisions) - Camera integration with Unity Cinemachine
- Supports both P2P and ServerDriven modes
- LZ4-compressed replay record / playback
Docs: Docs/Samples/Brawler.md
| Document | Contents |
|---|---|
| Docs/FEATURES.md | Full feature list |
| Docs/Specification.md | Engine specification (state machines · configuration · events · message protocol · formats) |
| Docs/GameDevWorkflow.md | Game-developer workflow (step-by-step) |
| Docs/GameDevAPI.md | Game-developer API status |
| Docs/SimulationConfigGuide.md | SimulationConfig recommended-value guide (per genre / platform) |
| Docs/BaseLibraries.md | List of base libraries used |
| Docs/Navigation.md | Deterministic navigation (FPNavMesh · A* · Funnel · ORCA) |
| Docs/Samples/ | Detailed Brawler sample documentation |
- Determinism first — no float; all simulation state is FP64 / integer / bool
- Zero-GC oriented — ref struct, object pools, cached fields, no LINQ
- Engine independence — the core is pure C# (no
UnityEnginereferences); Unity integration lives in an adapter layer - Minimal bandwidth — only inputs (commands) are sent; no state synchronization (only hash verification)
- Layer separation — strict separation between simulation callbacks (deterministic) and view callbacks (non-deterministic)