The first plugins with a real external dependency (Silk.NET) and the first that need a display. Both built, both verified by hand against a real window and a live GL context — not just "compiles." engine.windowing: - IEngineWindow, not IWindow — Silk.NET's own windowing type already owns that name; same lesson as the kernel's World -> GameWorld rename, applied proactively this time instead of hitting the build error first. docs/kernel-contract.md's §3 illustrative example updated to match. - Exposes the real Silk.NET IWindow directly (IEngineWindow.Native) rather than re-wrapping it — GL context creation and event pumping both need it, and hiding it buys nothing yet. engine.render: - Minimal: glClear + SwapBuffers against a hardcoded color, no mesh. Enough to prove M1's actual claim, which has nothing to do with triangles specifically: edit a plugin, rebuild just it, reload it while a real window stays open, see the change with no app restart. - No Contracts assembly — PluginManifest.Contracts is now nullable rather than forcing an empty assembly into existence just to satisfy the schema; PluginHost.Load skips the Default-ALC step when absent. Engine.Host: - --windowed alongside --headless: pumps window events plus Stage.Update/Stage.Render each frame instead of a bounded --frames loop. References Engine.Windowing.Contracts directly (never the implementation) to know how to drive that loop — same "Contracts are safe to share" pattern already proven for Sandbox.Echo.Contracts. - New: typing "r <plugin-id>" + Enter reloads that plugin live. Not a file watcher (still not built), but real Unload+Load through the same PluginHost path, against a running window — this is what actually exercised the hot-reload claim below. - IServiceRegistry gained TryRequire<T> so Engine.Host can ask "is a window available" without treating its absence as an error; a plugin's own Configure()/Shutdown() should keep using Require(). Verified end to end by hand: opened the window, watched it render its hardcoded color, edited RenderPlugin.cs's ClearColor, rebuilt only that project, typed "r engine.render" into the running process, and watched the color change with the same window and GL context still alive. Also found and documented a real platform gotcha along the way: on Wayland (unlike X11), a window with no committed buffer isn't shown at all, not even as a black rectangle — engine.windowing alone produces an invisible window; engine.render's first Clear+SwapBuffers is what actually makes it appear. Noted directly on RenderPlugin. Not covered by automated tests, deliberately: opening a real window needs a real display, which isn't safe to assume of every environment this runs in. ServiceRegistry.TryRequire<T> and the null-Contracts path in PluginHost are unit-tested; the window/render/reload flow is described here and was checked by hand instead. README status updated: M1 in progress, not done — engine.input and an actual drawn triangle (vs. a clear color) are still open. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1qPfzq8TDCUMFMV3UwV5N
52 lines
2.5 KiB
Markdown
52 lines
2.5 KiB
Markdown
# 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`](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`](docs/kernel-contract.md#7-written-by-an-agent-not-a-human).
|
||
|
||
## 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`](docs/kernel-contract.md#7-written-by-an-agent-not-a-human)
|
||
runs end to end.
|
||
|
||
**M1 in progress.** `engine.windowing` and a minimal `engine.render` (clear
|
||
color, no mesh yet) exist over Silk.NET, and 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, by hand, against a
|
||
live GL context. `engine.input` and an actual drawn triangle (vs. a clear
|
||
color) are still open. No physics yet — see the build order (M0–M4) in
|
||
[`docs/kernel-contract.md`](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](LICENSE)
|