EternalGarden.Rzeka.Dev
1.2.0
dotnet add package EternalGarden.Rzeka.Dev --version 1.2.0
NuGet\Install-Package EternalGarden.Rzeka.Dev -Version 1.2.0
<PackageReference Include="EternalGarden.Rzeka.Dev" Version="1.2.0" />
<PackageVersion Include="EternalGarden.Rzeka.Dev" Version="1.2.0" />
<PackageReference Include="EternalGarden.Rzeka.Dev" />
paket add EternalGarden.Rzeka.Dev --version 1.2.0
#r "nuget: EternalGarden.Rzeka.Dev, 1.2.0"
#:package EternalGarden.Rzeka.Dev@1.2.0
#addin nuget:?package=EternalGarden.Rzeka.Dev&version=1.2.0
#tool nuget:?package=EternalGarden.Rzeka.Dev&version=1.2.0
💦📜🏹 rzeka
🌟📜 Important: This README is a glimpse into rzeka, not the manual. Full documentation in Wiki – API reference, threading model, Eris, attributes & error handling.
A reactive event bus for C# that tracks causality.
rzeka ("river" in Polish) is a single-threaded event bus built on Rx.NET. Components yeet typed events into the river and act on the events flowing through it, without holding references to each other.
What makes rzeka different from a typical event bus is causality tracking: every event carries a record of the events that caused it.
You can ask any event – o little rabbit! where did you come from? – and get its full casual chain – what fox's chasing glare or carrots allure brought you here?
Thanks to Eris (rzeka's built-in debugger) you can read the entire story of what's happening in your system – real-time, in a browser, while your game runs (both in a game engine and in a build!).
https://github.com/user-attachments/assets/de5e608b-5123-47dd-99c4-996be5ff5259
⚗️ rzeka is single-threaded by design. This constraint guarantees consistent circumstance tracking. Async operations are handled within defined boundaries (see Shuttle).
Status: rzeka was originally built for sanctuary, a 3D journaling software shipped on Unity. It is currently being refactored alongside sanctuary's port to Godot. The core API is stable. Eris UI and Godot integration are evolving.
🪞 The metaphor
rzeka uses a river, textile and magic themed naming system: events are Matter, transformations are Spells, and a spell's ingredient-readiness is its Mana. The spell-defining methods (Strand, Loom, Shuttle, Weave, Pluck) follow textile-production vocabulary. Eris, the debugger, borrows her name from the Greek goddess of discord.
The metaphor is consistent, and once you let it work its magic the API becomes self-describing. The full vocabulary lives in the Grimoire.
A page from Witches by Daisuke Igarashi
💾 Installation
rzeka targets net8.0 and depends only on System.Reactive.
dotnet add package EternalGarden.Rzeka
For the optional browser-based Eris debugger (EternalGarden.Rzeka.Dev) and Godot's transitive-dependency quirks, go to the Installation page.
🌱 A taste
Summon one river at startup, then let typed events flow into it and react to them - no component holds a reference to any other. Here is a complete vertical slice: a witch is hexed, her ward weakens, its glow answers on screen.
using Rzeka;
// Events are Matter. Extend Matter to shape your own.
class HexCast : Matter
{
public int Potency { get; }
public HexCast(int potency) => Potency = potency;
}
// [HasState] matter remembers its last value and replays it to new subscribers.
[HasState]
class WardState : Matter
{
public int Strength { get; }
public WardState(int strength) => Strength = strength;
}
// Summon the river once, share its IRzeka reference with the systems that need it.
// mainThread is an IScheduler for your engine's main thread - see Getting Started.
IRzeka rzeka = new Spring().Create("Styx", mainThread);
// Collect your spells so you can let them all go together later.
CollectibleDisposable Q = new();
// Seed the ward at full strength.
rzeka.Pluck(this, new WardState(100));
// Strand: let every hex the enemy hurls flow into the river.
// OnHexCastAsObservable() is something using Observable.FromEvent() or similar
Q += rzeka.Strand(
this,
enemy.OnHexCastAsObservable().Select(hex => new HexCast(hex.Potency))
);
// Loom: weave each hex against the standing ward, conjuring its next state.
// The HexCast matter is the trigger - WithLatestFromMatter fires off the hexes, not the ward updates.
Q += rzeka.Loom<WardState, HexCast, WardState>(
this,
(ward, hexes) => hexes.WithLatestFromMatter(ward)
.Select(t => new WardState(t.Item2.Strength - t.Item1.Potency))
);
// Weave: the path ending spell - read the ward, let its glow update in game.
Q += rzeka.Weave<WardState>(
this,
ward => ward.Subscribe(w => wardGlow.SetIntensity(w.Strength))
);
Every WardState here automatically remembers the HexCast that diminished it as a circumstance, so Eris can later show the whole causal chain. That automatic causality tracking is the core functionality of rzeka - the API and Matter pages cover it fully.

📜🧚🏻♀️ rzeka code will make the characteristic waterfall 2D structures that go deep into your indentation while remaining very clear and readable. This depth might not be for everyone though. Personally I really prefer that to a 1D top-down wall of code-text, maybe you might like it too! Screenshot info: nvim, theme Aquavium, semitransparent background, CSharpier formatter.
📜🌱 Want a runnable project instead? See little-river, a tiny example Godot game built on rzeka.
🏹 Eris, the debugger
rzeka ships with a browser-based debugger that connects to your running game over WebSocket and shows matter flow and messages in real time – you don't need to build your own in-game UI! Eris records internally even in release builds (a feature to dump crash logs is underway). The WebSocket server is added only in dev builds via the EternalGarden.Rzeka.Dev package so you can easily strip it from your release builds.
Setup, the live UI, the demo, and structured logging via Whisper are all covered on the Eris page.
🔖 Crash reporting
When a spell fails in a player's game, the optional EternalGarden.Rzeka.Reporting package can send its causal story to a server: which matter led to the failure, which spells shaped it and when. Structure only, without the values inside your matter, and nothing is sent until the player agrees. The package just POSTs JSON; a reference backend on Azure lives in rzeka-reporting-azure.
Setup, consent, what a report contains and the privacy model are on the Crash Reporting page.
📜 License
See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- EternalGarden.Rzeka (>= 1.2.0)
- Fleck (>= 1.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.