Development:Structure: Difference between revisions
No edit summary |
(refix the link) |
||
| Line 1: | Line 1: | ||
This article describes the structure of the code of Librae. [The code itself is available on GitLab |
This article describes the structure of the code of Librae. [https://gitlab.com/librae/librae The code itself is available on GitLab]. |
||
Part of the game engine is called <code>rustorion</code>. It's the older name for the project, and intended to be the name of the engine, when it is fully separated from game logic. |
Part of the game engine is called <code>rustorion</code>. It's the older name for the project, and intended to be the name of the engine, when it is fully separated from game logic. |
||
Revision as of 10:28, 5 October 2024
This article describes the structure of the code of Librae. The code itself is available on GitLab.
Part of the game engine is called rustorion. It's the older name for the project, and intended to be the name of the engine, when it is fully separated from game logic.
Language
The game is written in Rust. It tries to be pure Rust, but some unique foreign components are still used, most prominently GTK4 for the user interface.
Main parts
- rustorion — game logic library
- librae-gui — gtk-based UI
- librae-cli — CLI tools
Game state
The game is structured to support trustless (except the server), simultaneous multiplayer. The game state is stored as a single universe structure (rustorion::Universe). Client receives a view which contains only the information the client is supposed to know. Client sends actions to the server that contain the information about what the client wants to do on the current turn. When all the players are ready (submitted their actions or it's a timeout), the server processes all the actions (they are designed to be executed independently of each other, so there are no race conditions), performs various updating on the universe and notifies clients that they can download new views.
In the future, clients might be able to download previous views to browse them at will or use for gathering statistics.
Entities and IDs
Most things, entities within the universe, or anything else that implements EntityStored have an ID. It is an unique identifier of an entity within the storing structure. Using EntityStored::get() and other similar methods you can obtain a reference to the entity by its ID. IDs are used to store links between entities through plain inclusion on a structure, or using types from rustorion::storage::links for more complex relations like many-to-many.
IDs are issued sequentially, but then the number is encrypted with Blowfish to ensure there is no leaking information about number of entities in an universe. Because the encryption key is stored in Universe, only Universe can issue new IDs.
Working with state
To modify the state, the only option is to work with the storage type (e.g. Universe) directly. However, this is not easy, you have to obtain IDs, use them to extract data, etc., manually. This appears to be the only way to keep the structure mutable and avoid using some kind of GC.
However, there are simplified interfaces for Universe and Universeview called rustorion::universe::interface::Universe and rustorion::universeview::interface::Universeview, respectively. These allow for object-oriented, safe examination of data and contain many useful routines. The intention is to use these to prepare the modifications, and later execute them on the storage type after dropping the interface.
RPC
Communication with client is done using CBOR-serialized RPC. It's rather haphazardly done using Tokio's example-like length-delimited codecs, and needs to be improved or replaced with something less monstrous than gRPC. Light macro usage might be in order.
Authentication
Clients authenticate themselves with client TLS certificates to the server. Currently BLAKE3 hashes of the client certs are used, this seems to work but is probably not the best idea.