# Reading the kits' memory in place (structures) How an engine, a tool or a module in another language reads what the kits write in memory, without going through JSON: the fixed structures of the entity rows and of the poses, their generated accessors for each language, and the check that refuses a library that lays them out differently. Readable offline: `gripforge docs structs`, or the MCP tool `gripforge_docs` (topic `structs`). ## What it is for A game runs its logic in the kit host (the logic modules on the web, the library `libgfkits` natively). Each frame an engine needs the entities, their transforms and some state. Asking for them as JSON copies and parses text every frame. The structures contract lets the engine read the same bytes the kit host wrote, **in place**: - the rows of the entity table of the shared zone (`ZoneEntityRow`: id, flags, position, yaw, then the columns of the game); - the interpolated poses of the entities being drawn (`PresentPose`); - the headers of the zone, the inputs, the events, and the free-shape values kits exchange (`FixedValue`, `FixedBlock`…). You need this guide if you write an engine adapter, a tool that reads the zone, or a kit module in a language other than Rust. A game that uses the catalogue never touches it. ## A layout GripForge defines - Integers are little-endian; a field lies at the next multiple of its alignment (the C layout rules); an address inside a `Fixed*` structure is 64 bits wide whatever the address space. - Nothing depends on a compiler or a language: any code that fills these bytes is a valid peer. - Every structure has a number and a version (`major.minor`). A structure added, or a field added in reserved bytes, is a minor; a size, an offset or a meaning changed is a major. - The reference of every structure, field by field with its offset, is `docs/kit-structures.md` (`gripforge docs kit-structures`). It is generated from one declaration file, and so is everything below. There are two tables, both at contract 1.0: | Table | Holds | Who checks it | | --- | --- | --- | | **Module structures** (`FixedValue`, `FixedPair`, `FixedBlock`, `Bytes`, `Ctx`, the `Zone*` structures) | what the kit modules share in the common memory | each kit module carries its table in a custom section `gripforge.structs`; the build compares it to the declaration, the loader refuses a module of another major or a later minor, by name | | **Engine table** (`ZoneEntityRow`, `PresentPose`, `ZoneWorldHeader`) | what an engine reads in `libgfkits` or in the logic module | the library answers it (`gfk_structs`); the accessors of each language check it once at start | ## Read the entity rows `gfk_zone` gives the table (`rows`, `row_bytes`, `capacity`, `used`, `owner`, `epoch`); row `i` starts at `rows + row_bytes * i`. The start of each row is a `ZoneEntityRow` (40 bytes): | At | Type | Field | Meaning | | --- | --- | --- | --- | | 0 | `u32` | `id` | the number of the entity's id (`e12` is 12); 0 an empty row, `0xffffffff` a row left by a destroyed entity | | 4 | `u32` | `flags` | bit 0: the entity has a transform | | 8, 16, 24 | `f64` | `x`, `y`, `z` | position, right handed, Y up | | 32 | `f64` | `yaw` | radians about Y | The columns of the game schema follow (`gfk_column` gives the offset, size and type of one by name). `epoch` is the clock value of the last change of the world: if it did not move, no row changed, and you read nothing. Valid until the next `gfk_*` call of the game (the zone may grow); the offsets are valid for the whole play. ## Read the poses of what you draw `gfk_present_poses(game, &n)` returns the interpolated pose of each presented entity, one `PresentPose` per entity of the snapshot and in its order (same fields as the row start: `id`, `flags`, `x`, `y`, `z`, `yaw`). Nothing is copied. It is valid until the next `gfk_present_frame` of the game. Start the presentation with the options that go with it (`gfk_present_start`, JSON): | Option | Effect | | --- | --- | | `"poses": "table"` | the entities of the snapshot carry no `transform`: read them from `gfk_present_poses` | | `"unchanged": "omit"` | with the table: a snapshot leaves out `entities` that did not change since the previous one, and after the first carries, in place of `vars`, only `varsSet: { path: value }` and `varsGone: [path]` when some changed; the engine keeps what it has. The entities carry no `velocity` | `gfk_copied(reset)` counts the bytes copied between the library and the engine since the last reset (every answer written to the output, every JSON text handed in); reading in place is what keeps it low. `gfk_physics_copied` is the same count for the engine's physics callbacks. ## The generated accessors Never edited by hand; regenerated from the declaration, and checked in the continuous integration. | Language | File | Use | | --- | --- | --- | | C# (Unity) | `GfStructs.g.cs` | explicit-layout structures read by pointer (`GfZoneEntityRow*`, `GfPresentPose*`); `GfStructs.Check(table, len, "libgfkits")` | | C++ (Unreal) | `GfStructs.g.h` | aligned structures held by `static_assert`; `GfStructs::Check`; `UGfGameHost::PresentPoses(Count)` | | GDScript (Godot) | `gf_structs.gd` | sizes, offsets and accessors over bytes (`GfStructs.check`); the extension reads in place in Rust | | Rust | `engine_structs.rs` (crate `gripforge-kitcall`) | `repr(C)` types held by `const` asserts; `check`; used by `libgfkits`, the Godot extension and the native player | | JavaScript (web) | `kit-structs.generated.mjs` | `DataView` views on the memory, shared or not; `checkStructs(table, library)`; `host.zoneRows()` | The Unity and Unreal accessors need no setup beyond using the package: the host checks the library once when it loads. ## The check at load The library answers its table with `gfk_structs(&len)`: the magic `GFST`, the version of the engine table, then every structure with its size and the offset of every field as its compiler laid them out. An accessor refuses, with a message that names the reason, a library: - without the table (`... answers no structures table (magic GFST missing): it predates the engine structures, update it`); - of **another major** ("the package and the library do not lay the memory out the same way; update them together"); - of an **earlier minor** than the package needs; - that lays a structure out differently from the declaration. Update the package and the library together; never try to read a library the check refused. ## Writing a module in another language A kit module that shares memory with the others must fill these bytes exactly. The rules to follow: 1. Take the layouts from `docs/kit-structures.md` (or the declaration it is generated from), never from your compiler. 2. Carry the table of the structures you use in a custom section `gripforge.structs`, so the loader can compare it. 3. Respect who owns each structure and how long it is valid: the page of each structure says whether the core or a kit writes it, and until when a pointer to it is valid. 4. Test your reading against the fixture of the contract: it holds every structure once, every field a value of its own, and each accessor must read the expected text line for line. For writing a kit in Rust, see `gripforge docs kit-third-party`. ## When it does not work | Message | What to do | | --- | --- | | `... answers no structures table` | the library is older than the package: update the library | | `engine structures 1.x expected, ... provides ...` (another major) | update the package and the library together | | `... the package needs a later minor than this library has` | update the library | | a structure `... laid out differently` | the library was built with another declaration: use the one shipped with your package |