SDK-First Architecture
GoudEngine is a Rust game engine with multi-language SDK support. All game logic lives in Rust. SDKs are thin wrappers: they marshal data and call FFI functions, never implementing logic of their own.
Layer Architecture
Dependencies flow DOWN only. A higher-numbered layer may import from lower layers; the reverse is a violation. The canonical layer definition lives in tools/lint_layers.rs.
Layer 1 (Foundation) : goud_engine/src/core/ — error types, math, handles, provider traits
Layer 2 (Libs) : goud_engine/src/libs/ — graphics backend, platform, native providers
Layer 3 (Services) : goud_engine/src/ecs/, assets/ — ECS, asset loading
Layer 4 (Engine) : goud_engine/src/sdk/, rendering/, context_registry/ — game API, render orchestration
Layer 5 (FFI) : goud_engine/src/ffi/, wasm/ — C-ABI exports consumed by external SDKs
Beyond the engine crate, two additional layers complete the picture:
- SDKs (
sdks/) — language-specific wrappers over FFI for all SDK languages - Apps (
examples/) — example games that use SDK APIs
Each layer knows nothing about the layers above it. ffi/ may import from core/, sdk/, ecs/, and assets/. It must never import from sdks/.
Layer Enforcement
A lint-layers binary scans use crate:: imports across all source files and fails the build if any upward dependency is found. It runs in two places:
codegen.shstep 2 (cargo run -p lint-layers)- Pre-commit hook (via
.husky/)
Key Files by Layer
Core (goud_engine/src/core/)
| File | Purpose |
|---|---|
types.rs | Shared FFI-compatible types (FfiVec2, GoudContextId, GoudResult) |
context_registry.rs | Thread-safe registry mapping GoudContextId → engine instances |
component_ops.rs | Generic component CRUD used by FFI component handlers |
math.rs | Vec2, Vec3, Color, Rect, Mat3x3 with #[repr(C)] for FFI |
error.rs | GoudError, GoudErrorCode, GoudResult |
FFI (goud_engine/src/ffi/)
Each domain has its own file. All public functions are #[no_mangle] pub extern "C".
| File | Domain |
|---|---|
context.rs | Engine context create/destroy |
entity.rs | Entity spawn/despawn |
component.rs | Generic component add/remove/query |
component_transform2d.rs | Transform2D component operations |
component_sprite.rs | Sprite component operations |
window.rs | Window management, frame lifecycle |
renderer.rs | 2D rendering |
renderer3d.rs | 3D rendering |
input.rs | Input state queries |
collision.rs | Collision detection |
types.rs | Re-exports core/types.rs types at the FFI boundary |
SDK (sdks/)
All SDK languages live under sdks/. Each has a generated wrapper surface and a thin hand-written API layer. See the sdks/ directory for the full list of supported languages.
Codegen Pipeline
All SDK source files under sdks/*/generated/ and sdks/typescript/src/generated/ are auto-generated. Do not edit them by hand.
goud_sdk.schema.json — universal type/method definitions
ffi_mapping.json — maps schema methods -> C ABI function names + signatures
ffi_manifest.json — auto-extracted from Rust source by build.rs (each cargo build)
goud_engine.h — auto-generated C header at codegen/generated/ (each native cargo build)
|
v
codegen/gen_*.py generators
|
v
sdks/*/generated/ output directories
Run the full pipeline:
./codegen.sh
The script runs scaffolding, build, validation, and generation steps in order. See codegen.sh for the current step count and sequence. Validation gates (header checks, lint-layers, coverage, schema consistency) abort the pipeline on failure.
Schema Files
codegen/goud_sdk.schema.json — the single source of truth. Defines:
types— value types (e.g.,Color,Vec2,Transform2D) with fields and factory methodsenums— enumeration types (e.g.,Key,MouseButton) with values and optional platform mapstools— high-level objects likeGoudGamewith constructor, destructor, lifecycle hooks, and methods
codegen/ffi_mapping.json — implementation details. Defines:
ffi_types— how each schema type maps to a C struct name and its fieldsffi_handles— opaque handle types (GoudContextId,GoudTextureHandle)ctypes_mappings— Python ctypes annotations for pointer typestools— per-method mapping from schema method name to FFI function name and parameters
codegen/ffi_manifest.json — auto-generated by build.rs. Contains the full list of #[no_mangle] extern "C" functions extracted from goud_engine/src/ffi/. Used by validate_coverage.py to detect unmapped functions.
Shared Utilities (codegen/sdk_common.py)
All generators import sdk_common.py for:
load_schema()/load_ffi_mapping()— JSON loading- Name converters:
to_pascal(),to_snake(),to_camel(),to_screaming_snake() - Type maps:
CSHARP_TYPES,PYTHON_TYPES,TYPESCRIPT_TYPES,CTYPES_MAP,CSHARP_FFI_TYPES write_generated(path, content)— writes output files, creating parent directories
Key Design Decisions
Rust-first. Logic is never duplicated in SDKs. If a SDK method does anything beyond marshaling and calling an FFI function, that logic belongs in Rust.
Single schema, multiple generators. Adding a type to goud_sdk.schema.json causes it to appear in every SDK on the next codegen run. Each generator (codegen/gen_*.py) applies language-appropriate naming conventions. Run ./codegen.sh to regenerate all SDKs.
C# bindings are doubly generated. NativeMethods.g.cs is produced by csbindgen on every cargo build. The higher-level C# wrapper classes in sdks/csharp/generated/ are produced by gen_csharp.py. The two files work together: csbindgen handles the raw [DllImport] declarations, and the Python generator handles the public wrapper API.
Context handles, not pointers. All FFI calls take a GoudContextId (an opaque u64) rather than a raw pointer. The context registry resolves handles to engine instances under a mutex. This prevents use-after-free and type confusion across the FFI boundary.
Error propagation. FFI functions return GoudResult (an i32) — 0 for success, negative for error. Detailed error messages are stored in thread-local storage and retrieved via goud_get_last_error_message().
What Goes Where
| You want to… | Where to put it |
|---|---|
| Add a new game mechanic | goud_engine/src/ (core, ecs, or assets) |
| Expose a mechanic to SDKs | Add #[no_mangle] extern "C" function in goud_engine/src/ffi/ |
| Add a new type to all SDKs | Add to codegen/goud_sdk.schema.json, run ./codegen.sh |
| Change method naming in one SDK | Edit the relevant generator in codegen/gen_<lang>.py |
| Add a new SDK language | See Adding a New Language |