EmilandClaude Sonnet 5 45daa6e114 M2, part one: scene format — World actually saves and loads now
The first half of M2's "done when" (a scene loads and saves) rather
than the whole milestone — the asset hot-reload half is a comparably
sized, separate chunk of work, staged on its own rather than crammed
in alongside this.

SceneFormat replaces WorldDumper rather than sitting next to it:
there was never a real reason for "what an agent reads to check a
frame" (the existing --dump) and "what a scene file actually is" to
be two different JSON shapes, and keeping them one removes the
question of which shape a save/load round trip is supposed to match.
Moved from Engine.Kernel.Diagnostics to Engine.Kernel.World to match —
this is core content loading now, not a debug tool that happens to
also serialize things.

Components are tagged "TypeFullName, AssemblyName" (partial-name
form, deliberately no version) so Type.GetType resolves them against
whatever's loaded regardless of an incidental version bump on the
plugin that defines them — full four-part AssemblyQualifiedName would
have made every saved scene brittle against that. Loading a scene
whose component type isn't loaded fails loudly, naming the missing
type, rather than silently dropping data.

New kernel API this needed: GameObject.AddComponent(Component) —
attaches an already-constructed instance, for a caller (the
deserializer) that only has a runtime Type from a file, not a
compile-time T. Deserializing straight into a real instance via
JsonSerializer.Deserialize(json, componentType) and attaching that is
simpler and more certain than constructing an empty component through
reflection and then trying to populate it after the fact.

Engine.Host: --scene now does something (was an explicit "not
implemented yet" since the very first CLI pass) — loads additively
after every plugin in --project, since a scene's component type tags
only resolve once the plugin defining them has loaded its Contracts
assembly.

Verified beyond the round-trip unit tests: two separate real CLI runs,
sandbox.echo both times. Run 1 ticks 3 frames and dumps a scene
(Ping.Count: 3). Run 2 loads that scene fresh alongside its own
newly-seeded Ping (Count: 0) and ticks 2 more frames — dump shows
Count: 2 for the fresh one and Count: 5 for the loaded one. Not just
"the file round-trips" — the loaded component's state kept being a
real, live, Scheduler-ticked object across the save/load boundary.

44 tests in Engine.Kernel.Tests now (up from 40 — 3 carried over from
WorldDumperTests plus 4 new round-trip/error-path tests), 8 in
Engine.ConformanceHarness unaffected. All green on a clean build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N
2026-09-02 05:07:14 +03:00

Lingua Engine

A modular, plugin-first game engine built around one idea: the kernel is a shared language, not a shared implementation. Everything the engine can do — rendering, physics, audio, even the editor itself — is a plugin that speaks that language. The kernel only defines the vocabulary plugins use to understand each other.

Built for Linux and Windows, in C#/.NET, with two goals that shape every design decision:

  • Fast iteration. No Unity-style domain reload. Plugins hot-reload their compiled code without resetting game state, because state never lives in plugin code to begin with — see docs/kernel-contract.md.
  • A small, frozen kernel. Everything else — including the parts most engines treat as core — is a plugin, versioned and replaceable per project.

How this gets built

Most of the code here — kernel and plugins alike — is written by an LLM coding agent rather than by hand. That's not incidental: it's a design input. It's why the object model is GameObject/Component instead of a hand-rolled ECS, why registration is verbose and explicit instead of convention-based, and why the engine has a headless, scriptable introspection surface no classic editor bothers with — see docs/kernel-contract.md.

Status

M0 done. The kernel — World (GameObject/Component, type-indexed queries), Schedule (stage execution, conflict batching, debug-mode access enforcement), PluginHost (two-ALC load/unload, verified leak-free over 200 cycles), and a headless CLI (engine run --headless ... --dump) — all exist and are tested. The full agent loop from docs/kernel-contract.md#7 runs end to end.

M1 done. engine.windowing, engine.render (a real shader-drawn triangle, not just a clear color), and engine.input all exist over Silk.NET. The milestone's actual claim — edit a plugin's code, rebuild just it, reload it while a real window stays open, see the change with no app restart — is proven against a live GL context: two PNGs of the same running window, before and after a live reload, orange triangle then green, same process the whole time. IScreenCapture (engine.render) reads the frame back from the GPU and writes it to a file with a hand-rolled PNG encoder — no SixLabors.ImageSharp (its license isn't MIT/Apache) and no desktop screenshot tool, so this is checkable without a screen at all, exactly the introspection story docs/kernel-contract.md#7 argues for.

The kernel is closed. All four questions the original design left open — Time/Log's home, whether the Event Bus is real infrastructure or event-components, whether frame stages are fixed or plugin-extensible, and the data-oriented-fast-path question — are resolved, each with working code behind it, not just an answer written into the doc. Time and the Event Bus (Publish/Subscribe, leak-safe the same way Schedule already is) both shipped; sandbox.echo subscribes to PluginLoaded for real, so the 200-cycle leak test now proves EventBus doesn't leak too, not just Schedule. See the resolutions in docs/kernel-contract.md — one of the four (the fast path) is deliberately still open, but with a concrete trigger condition instead of a deadline, not left vague.

No physics yet — see the build order (M0M4) in docs/kernel-contract.md for what's next.

Design and implementation are argued over in the same place: the doc is still the thing to disagree with before code changes to match.

License

MIT

S
Description
A modular, plugin-first game engine: a small frozen kernel that is a shared language, everything else — including the editor — a plugin.
Readme MIT
1.9 MiB
Languages
C# 93.6%
C 4.5%
Shell 1.2%
CMake 0.7%