A work‑in‑progress, modular C++20 engine focused on foundational architecture, technical design, and clean systems engineering rather than commercial features or production‑ready tooling.
XGE is an exploration of modern engine architecture: reflection‑driven data, modular runtime systems, dual‑runtime scripting (CoreCLR + NativeAOT), and strict layering. The goal is to build a solid, extensible foundation that can grow into a full engine over time.
The project emphasizes:
- strict layering
- reflection‑driven data
- modular runtime systems
- dual scripting runtimes (CoreCLR + NativeAOT, extendable to support other language like Squirrel, Lua, etc)
- editor integration
- clean, STL‑free public API surfaces
- Overview
- Design Philosophy
- Architecture
- Current Engine Capabilities
- Current Work‑In‑Progress
- Rendering Architecture
- Network Serialization (Planned)
- Scripting Architecture
- Repository Structure
- Reflection System
- JSON Serialization
- Runtime Flow
- Roadmap
- License
XGE is a modular, reflection‑driven, cross‑platform engine foundation designed for long‑term scalability.
It prioritizes clean architecture, strict layering, and runtime modularity over monolithic engine design.
XGE is built to support:
- multiple scripting runtimes
- multiple rendering backends
- editor/runtime separation
- deterministic data pipelines
- future network replication
- future visual scripting
- future hot‑reload workflows
No platform code in core.
No renderer code in scripting.
No STL in public headers.
Tree‑sitter generates metadata used for:
- JSON serialization
- editor property panels
- network replication (future)
- deterministic data pipelines
Everything is a module:
- renderer
- scripting runtime
- editor kernel
- future physics/audio/gameplay
Load modules at runtime.
Swap renderer or scripting backend without recompiling the engine.
xgBase → Base types, macros, platform detection
xgPlatform → Platform backends (Win32, SDL)
xgCore → Engine runtime
Renderers → DX12, Vulkan (modules)
Script Hosts → CoreCLR, NativeAOT (modules)
Editor → Native editor kernel + managed editor runtime
- Modular engine with dynamic module loading
- Reflection‑driven configuration
- JSON serialization via reflection metadata
- Strict layering
- STL‑free public API surface
- SDL3 window backend
- Win32 file backend
- Cross‑platform module loader
- Event system (SDL →
xgEvent)
- DirectX 12 renderer module
- Swap chain, command queue, render targets
- Clear + present rendering loop
- Dual‑runtime scripting: CoreCLR + NativeAOT
- Unified
ScriptModuleinterface - Managed editor runtime
- Native editor kernel module
- Full ImGui integration
- Docking + multi‑viewport
- Viewport, Outliner, Properties, Timeline, Assets panels
XGE’s runtime is built around HSM (Hierarchical Script Modules) — a structured tree of ScriptModules that defines ownership, routing scope, and execution domains.
The system consists of:
ScriptTree — hierarchical tree of ScriptModules (HSM)(DONE)Messenger — message routing backbone(DONE)Route Traversal — parent/child/sibling routing rules(DONE)Codec Registry — dynamic message encoding/decoding(DONE)- Dispatch Groups — thread‑affinity execution domains (IN PROGRESS)
Together, these form XGE’s core runtime architecture.
ScriptTree stores every ScriptModule in a strict hierarchy:
- parent → child relationships
- sibling traversal
- domain grouping (Main, Worker, Pinned, Dedicated)
- deterministic update order
- scoped message routing
Each ScriptModule is a node in the tree, enabling:
- hierarchical updates
- scoped messaging
- clean separation of systems
- future hot‑reload boundaries
Messenger delivers ScriptMessage objects across the ScriptTree using explicit Route objects.
Routing is deterministic and handled by ScriptTree; encoding/decoding is handled by the Codec Registry.
Messages are delivered by constructing a Route and passing it to Messenger::Send.
ScriptMessage msg{ PlayerDamaged{ .Amount = 10 } };
// Direct message to a specific ScriptModule
Messenger->Send(msg, Route::Direct("Player"));
// Broadcast to all children of a ScriptModule
Messenger->Send(msg, Route::ChildrenOf("UIRoot"));
// Bubble upward to parents
Messenger->Send(msg, Route::ParentOf("Inventory"));
// Send to siblings
Messenger->Send(msg, Route::SiblingsOf("EnemyAI"));
// Filtered routing (predicate receives ScriptModule*)
Messenger->Send(msg, Route::Filtered([](ScriptModule* m) {
return m->GetId() == "HUD";
}));
// Global broadcast
Messenger->SendToAll(msg);Messages can be routed using simple rules:
- ToParent — bubble upward
- ToChildren — broadcast downward
- ToSiblings — lateral routing
- ToDomain — cross‑thread dispatch
- ToSpecificNode — direct targeting
Example:
Messenger->Send(msg, Route::ToParent);
Messenger->Broadcast(msg, Route::ToChildren);
Messenger->Send(msg, Route::ToDomain(Thread::Worker));The CodecRegistry is responsible for encoding and decoding all ScriptMessage payloads.
It supports both native C++ types and dynamic types (DynamicObject), using reflection metadata (TypeSchema) generated by the TypeRegistry.
CodecRegistry provides:
- native encoders/decoders (registered per typeName)
- dynamic JSON encode/decode for
DynamicObject - lookup of encoder/decoder functions
- unified public
Encode/Decodeentry points - integration with Messenger and ScriptTree
Native types register their encoder/decoder functions:
registry.RegisterEncoder("PlayerDamaged", Encode_PlayerDamaged);
registry.RegisterDecoder("PlayerDamaged", Decode_PlayerDamaged);When Messenger delivers a message, CodecRegistry selects the correct encoder based on the message’s typeName.
Dynamic types (DynamicObject) do not register encoders.
CodecRegistry automatically falls back to JSON‑based dynamic encoding:
EncodeDynamic(obj, outMessage)DecodeDynamic(message, schema, outObj)
This allows:
- editor inspection
- flexible message formats
- runtime‑generated message types
- future network serialization
All message encoding flows through two entry points:
bool Encode(const char* typeName,
const void* src,
const TypeSchema* schema,
ScriptMessage& outMessage) const;
bool Decode(const char* typeName,
const ScriptMessage& message,
const TypeSchema* schema,
void* dst) const;These functions:
- select native or dynamic codec
- serialize the payload into
ScriptMessage - deserialize payload into native struct or
DynamicObject
Messenger does not serialize messages itself.
Instead, before delivery:
- Messenger calls
CodecRegistry::Encode - ScriptTree routes the encoded message
- Receiving ScriptModule calls
CodecRegistry::Decode - The decoded object is passed to the handler method
This ensures:
- consistent message formats
- reflection‑driven serialization
- safe cross‑module communication
- future network replication compatibility
Dispatch groups define execution domains:
- Input
- Gameplay
- Animation
- RenderPrep
- Async
- Custom
Modules declare dispatch groups via attributes (C#) or macros (C++).
The engine handles scheduling and thread affinity.
The HSM + ScriptTree + Messenger + Route Traversal + Codec Registry + Dispatch Groups form XGE’s core runtime architecture.
They provide:
- deterministic module updates
- safe multi‑threaded messaging
- structured message encoding
- hierarchical routing
- clean separation of systems
- future‑proof foundations for AI, gameplay, editor tooling, and networking
Validates:
- command queue + allocator
- descriptor heaps
- swap chain
- GPU/CPU sync
- render targets
Will validate:
- cross‑platform rendering
- explicit sync
- descriptor sets
- pipeline layout abstraction
- multi‑queue support
Both backends will share:
- unified renderer interface
- FrameGraph
- resource abstractions
- enumerate fields
- identify replicated fields
- serialize only required fields
- apply deltas deterministically
- compare previous vs current state
- emit only changed fields
- primitive packing
- boolean compression
- enum packing
- optional string hashing
XG_FIELD(meta=Replicated)
XG_FIELD(meta=Replicated | Interpolated)- method lookup
- parameter serialization
- stub generation
- hot reload
- reflection
- dynamic assembly loading
- debugging
- full .NET ecosystem
- zero dependencies
- instant startup
- no JIT
- native debugging
- predictable performance
Both runtimes implement:
class ScriptModule {
public:
virtual bool Init(ScriptEngine* engine) = 0;
virtual void Update(float dt) = 0;
virtual void Shutdown() = 0;
virtual bool IsValid() const = 0;
};XGE/
├── docs/ # Documentation, diagrams, design notes
├── gameroot/ # Runtime deployment root
│ ├── bin/ # Native binaries (engine + modules)
│ ├── editor/ # Editor assets (fonts, UI resources)
│ └── coreclr/ # CoreCLR runtime + managed assemblies
│ └── Microsoft.NETCore.App/
│ └── 10.0.8/
├── xgEngine/ # Engine source tree
│ ├── 3rdparty/ # External libraries (SDL, ImGui, etc.)
│ ├── tools/ # Engine tools (reflection generator, etc.)
│ │ └── xgReflectGen/
│ ├── xgBase/ # Base types, macros, platform detection
│ ├── xgCommon/ # Shared utilities (native + C#)
│ ├── xgCore/ # Core engine runtime
│ ├── xgPlatform/ # Platform backends (Win32, SDL)
│ ├── xgRendererDX12/ # DirectX 12 renderer module
│ ├── xgScriptCoreCLR/ # CoreCLR scripting host module
│ ├── xgScriptNative/ # NativeAOT scripting host module
│ ├── xgEditorKernel/ # Native editor kernel module
│ ├── xgUtility/ # Utility layer (platform helpers, etc.)
│ │ └── platform/
│ └── xge/ # Engine executable (launcher)
- Tree‑sitter C++
- generates
.generated.h - produces
RawFieldInfoarrays - supports bool/int/float/const char*
- uses
offsetof()
Example:
XG_SERIALIZABLE()
struct EngineConfig {
XG_FIELD()
const char* RendererModule = nullptr;
};- custom wrapper over nlohmann::json
- STL only in
.cpp - reflection‑driven serialization
- load config
- load renderer module
- load scripting module
- initialize systems
- fixed update
- variable rendering
- ScriptHost update
- module updates
- ScriptHost shutdown
- renderer shutdown
- platform shutdown
- Dispatch Groups
- HSM tooling
- messaging inspector
- editor integration
- hot reload boundaries
- scene graph
- component system
- property inspector
- enum/array reflection
- FrameGraph
- GPU‑driven rendering
- full editor
- shader graph
- hot‑reloadable C# gameplay
- network replication
- RPC system
- job system
- Vulkan parity
MIT License.

