From d3f07df8cc4cef318a10210e95ab2cdbd06a8db2 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 13 Jun 2026 06:27:59 +0100 Subject: [PATCH] Rename runtime state documents --- Aetheria.State.Smoke/Program.cs | 8 +++---- Aetheria.State/AetheriaDocumentRegistry.cs | 8 +++---- Aetheria.State/AetheriaStateNode.cs | 6 ++--- ...ts.cs => AetheriaRuntimeStateDocuments.cs} | 12 +++++----- Aetheria.State/README.md | 13 +++++----- README.md | 24 ++++++++----------- 6 files changed, 34 insertions(+), 37 deletions(-) rename Aetheria.State/Documents/{AetheriaSaveDocuments.cs => AetheriaRuntimeStateDocuments.cs} (93%) diff --git a/Aetheria.State.Smoke/Program.cs b/Aetheria.State.Smoke/Program.cs index 9477c29da..77f984163 100644 --- a/Aetheria.State.Smoke/Program.cs +++ b/Aetheria.State.Smoke/Program.cs @@ -85,7 +85,7 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s SampleNames = ["Ada", "Grace"] }); - await node.PutSavedRunAsync(runKey, new AetheriaSavedRun + await node.PutRunStateAsync(runKey, new AetheriaRunState { RunId = "smoke", IsTutorial = false, @@ -122,7 +122,7 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether var nameFile = await reopened.GetNameFileAsync(nameFileKey); var quarantine = await reopened.GetLegacyCatalogQuarantineAsync(); var playerSettings = await reopened.GetPlayerSettingsAsync(); - var savedRun = await reopened.GetSavedRunAsync(runKey); + var runState = await reopened.GetRunStateAsync(runKey); if (world?.WorldId != "aetheria") { @@ -154,9 +154,9 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether throw new InvalidOperationException("Player settings did not survive flush/reopen."); } - if (savedRun?.RunId != "smoke" || savedRun.ActionBarBindings.Length != 1) + if (runState?.RunId != "smoke" || runState.ActionBarBindings.Length != 1) { - throw new InvalidOperationException("Saved run did not survive flush/reopen."); + throw new InvalidOperationException("Run state did not survive flush/reopen."); } } diff --git a/Aetheria.State/AetheriaDocumentRegistry.cs b/Aetheria.State/AetheriaDocumentRegistry.cs index 714de18af..226a1289a 100644 --- a/Aetheria.State/AetheriaDocumentRegistry.cs +++ b/Aetheria.State/AetheriaDocumentRegistry.cs @@ -33,8 +33,8 @@ public static class AetheriaDocumentRegistry CultNetDocumentBinding.ForDocument(registry), CultNetDocumentBinding.ForDocument(registry), CultNetDocumentBinding.ForDocument(registry), - CultNetDocumentBinding.ForDocument(registry), - CultNetDocumentBinding.ForDocument(registry), + CultNetDocumentBinding.ForDocument(registry), + CultNetDocumentBinding.ForDocument(registry), CultNetDocumentBinding.ForDocument(registry) }); } @@ -50,8 +50,8 @@ public static class AetheriaDocumentRegistry typeof(AetheriaMigrationLedger), typeof(AetheriaLegacyCatalogQuarantine), typeof(AetheriaPlayerSettings), - typeof(AetheriaSavedRun), - typeof(AetheriaSavedZone), + typeof(AetheriaRunState), + typeof(AetheriaZoneState), typeof(AetheriaEntitySnapshot) ]; } diff --git a/Aetheria.State/AetheriaStateNode.cs b/Aetheria.State/AetheriaStateNode.cs index 2f66e982f..b21b4e1be 100644 --- a/Aetheria.State/AetheriaStateNode.cs +++ b/Aetheria.State/AetheriaStateNode.cs @@ -130,14 +130,14 @@ public sealed class AetheriaStateNode : IAsyncDisposable, IDisposable return Database.GetAsync(new CultRecordKey("global:aetheria.player_settings.v1")); } - public Task> PutSavedRunAsync(CultRecordKey key, AetheriaSavedRun run) + public Task> PutRunStateAsync(CultRecordKey key, AetheriaRunState run) { return Database.PutAsync(key, run); } - public Task GetSavedRunAsync(CultRecordKey key) + public Task GetRunStateAsync(CultRecordKey key) { - return Database.GetAsync(key); + return Database.GetAsync(key); } public Task FlushAsync(bool soft = false) diff --git a/Aetheria.State/Documents/AetheriaSaveDocuments.cs b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs similarity index 93% rename from Aetheria.State/Documents/AetheriaSaveDocuments.cs rename to Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs index cdb6af7d6..d103968a1 100644 --- a/Aetheria.State/Documents/AetheriaSaveDocuments.cs +++ b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs @@ -13,16 +13,16 @@ public sealed class AetheriaPlayerSettings public string Name { get; set; } = "Aetheria player settings"; [Key(1)] - [CultReference(typeof(AetheriaSavedRun))] + [CultReference(typeof(AetheriaRunState))] public string ActiveRunKey { get; set; } = ""; [Key(2)] public string LastUpdatedAtUtc { get; set; } = ""; } -[CultDocument("aetheria.saved_run", "aetheria.saved_run.v1")] +[CultDocument("aetheria.run_state", "aetheria.run_state.v1")] [MessagePackObject] -public sealed class AetheriaSavedRun +public sealed class AetheriaRunState { [Key(0)] [CultName] @@ -47,7 +47,7 @@ public sealed class AetheriaSavedRun public int[] DiscoveredZoneIndices { get; set; } = []; [Key(7)] - [CultReference(typeof(AetheriaSavedZone), many: true)] + [CultReference(typeof(AetheriaZoneState), many: true)] public string[] ZoneKeys { get; set; } = []; [Key(8)] @@ -60,9 +60,9 @@ public sealed class AetheriaSavedRun public string UpdatedAtUtc { get; set; } = ""; } -[CultDocument("aetheria.saved_zone", "aetheria.saved_zone.v1")] +[CultDocument("aetheria.zone_state", "aetheria.zone_state.v1")] [MessagePackObject] -public sealed class AetheriaSavedZone +public sealed class AetheriaZoneState { [Key(0)] [CultName] diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index 4b98d546f..81bf640f3 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -9,10 +9,11 @@ tree, RethinkDB, JsonKnownTypes, or Newtonsoft.Json. The first live state file is `GameData/aetheria-world.cc`. Legacy files such as `GameData/AetherDB.msgpack` are migration inputs only. -The typed save model is split into player settings, saved runs, saved zones, -entity snapshots, item slots, weapon groups, action-bar bindings, and stat -grids. Do not preserve `SavedGame` or `EntityPack` as opaque payloads in the -new store; they are source shapes for migration, not portable state authority. +The typed runtime state model is split into player settings, run state, zone +state, entity snapshots, item slots, weapon groups, action-bar bindings, and +stat grids. Do not preserve `SavedGame` or `EntityPack` as opaque payloads in +the new store; they are source shapes for migration, not portable state +authority. The Unity client no longer writes `PlayerSettings.msgpack`, `.loadout`, or `.zone` files. Until `Aetheria.State` is available to Unity as a runtime Verse @@ -21,8 +22,8 @@ disabled. That is intentional: the missing runtime package is the owner gap, and the old bespoke file formats must not keep acting as durable truth while the state spine is being rebuilt. The old `SavedGame`/`SavedZone` DTOs and `Galaxy` loader constructor have also -been deleted; the only save model left is the typed `AetheriaSavedRun` document -family in this project. +been deleted; the new document family is live Verse run/zone state, not a +bespoke save-file format. Keyboard layout rendering now reads the checked-in Unity text asset directly. The old generated `GameData/KeyboardLayouts/*.msgpack` cache was deleted; layout diff --git a/README.md b/README.md index 6ae3c06c4..af4b1526c 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ If you want to chat, please join [our Discord server](https://discord.gg/trbteNj 5. [Contributing](#Contributing) - [Getting the Files](#Getting-the-Files) - [Choosing a Task](#Choosing-a-Task) - - [Database Editor Tools](#Database-Editor-Tools) - - [Connecting to RethinkDB](#Connecting-to-RethinkDB) + - [Typed State Tools](#Typed-State-Tools) + - [Importing Legacy Catalog Data](#Importing-Legacy-Catalog-Data) - [Editing Items](#Editing-Items) - [Testing Locally](#Testing-Locally) - [Debug Console](#Debug-Console) @@ -68,9 +68,7 @@ There are two solutions in this repository. One is a Unity project containing th ### Third Party Libraries -Client-Server communication is implemented using [LiteNetLib](https://github.com/RevenantX/LiteNetLib), a semi-reliable UDP transport library which we use to transmit [MessagePack](https://github.com/neuecc/MessagePack-CSharp) over the wire. - -Aetheria uses [RethinkDB](https://rethinkdb.com/) for data persistence. To make this possible, all persistent data is marked with attributes for both MessagePack and [JSON.Net](https://www.newtonsoft.com/json) serialization. During operation, the client does not communicate with the database server directly, only the game server does that; the game server caches data relevant to the game and sends it to the clients. +Aetheria's new persistence spine is `Aetheria.State`: typed CultCache `.cc` documents exposed through CultNet/CultMesh. Legacy MessagePack catalog files are migration inputs only; they are fingerprinted and mapped into typed item, faction, and name-file documents before runtime systems are allowed to treat them as state. ### Programming Paradigms @@ -78,7 +76,7 @@ The codebase makes heavy use of C#'s [Language Integrated Queries (LINQ)](https: ### Data Structures -All of the persistent state classes inherit from the DatabaseEntry class, which uses a GUID as each entry's primary key. Whenever a reference to a database entry must be held, it should be stored as a GUID and when needed, retrieved directly from the DatabaseCache held by the appropriate manager, usually ItemManager for references to ItemData. The ItemManager contains GetData helper methods for retrieving the ItemData for the various subclasses of ItemInstance. +Persistent state belongs in typed CultCache documents with explicit record keys and CultNet schema bindings. Legacy `DatabaseEntry` identities may appear as migration provenance while the Unity runtime is being lowered onto the new state spine, but they are not the durable state owner. #### Equipment @@ -114,23 +112,21 @@ We are organizing according to an [Agile development](https://en.wikipedia.org/w You don't have to be a programmer to contribute, either! We have issue labels for and very much welcome contributions from [writers](https://github.com/rwvens/Aetheria-Economy/labels/worldbuilding) and [game designers](https://github.com/rwvens/Aetheria-Economy/labels/game%20design). -### Database Editor Tools +### Typed State Tools -In order to facilitate the creation and maintenance of game data, there is a Unity editor utility which communicates directly with RethinkDB. You can access the tools by selecting Window/Aetheria Database Tools in Unity's menu. This will cause two windows to appear, the Database List View and the Database Inspector. +The state spine lives in `Aetheria.State`. It defines CultCache documents, CultNet schema bindings, an embedded state node, and smoke coverage for writing, flushing, reopening, and reading `GameData/aetheria-world.cc`. -#### Connecting to RethinkDB +#### Importing Legacy Catalog Data -At the top of the list view there is a text field where you can enter the URL of the database server. When you click connect, the editor will download and cache all of the items in the game, as well as subscribe to the changefeed. The list should now populate with items. For access to our database servers and therefore live game data, please contact us; it would be dangerous to make our actual database URL public! +Use `Aetheria.State.Import` to quarantine the checked-in legacy catalog files and map stable MessagePack payload fields into typed CultCache state. The importer records path, size, and SHA-256 provenance for `GameData/AetherDB.msgpack` and `GameData/NameFile/*.msgpack`, then emits typed item, faction, and name-file records for the fields that have earned migration authority. #### Editing Items -You can unfold the categories of items in the list view to see what items exist. If you select an item, the Database Inspector will populate with all of the available fields of that item. Any changes you make in the Inspector will automatically be pushed to RethinkDB. If you've connected to the production database, this will update the stats of in-game items in real-time! +Item editing should target typed CultCache documents and CultMesh/Eve surfaces. The old Unity database inspector has been removed from authority; if it returns, it should be a lowering over the state spine, not a separate store. ### Testing Locally -Testing the game entirely offline doesn't require running the economy server, but you still need to download the database contents. In the database list view, click the "Connect" button. Once the tools are finished syncing, which can take a while (there's a progress bar), you can click "Save" to create a local backup of the entire database. If you enter Play mode in the "ARPG" scene, the game will use that local copy instead of requiring a connection to the master server. - -Note that this process needs to be repeated every time the data model changes or if you wish to test the game with updated database contents. +Testing the game entirely offline should use typed local state under `GameData/aetheria-world.cc`. Until the Unity runtime is fully lowered onto `Aetheria.State`, the checked-in legacy catalog files remain migration inputs and should be regenerated only by explicit import tooling. ### Debug Console