temper 0.2.0

A simulation-first Rust game engine
Documentation
# Modularity Rules

This file defines rules for keeping the architecture modular.

## 1. Core must stay small

The core should contain only concepts required by nearly every subsystem.

Allowed in core:

- App
- Plugin
- World
- Entity
- Component
- Resource
- System
- Schedule
- Commands
- Events
- Time/logging basics

Avoid putting rendering, physics, audio, editor, or game-specific logic in core.

## 2. Subsystems should be plugins

Rendering, input, assets, physics, audio, UI, and editor functionality should be added through plugins.

Example:

```rust
App::new()
    .add_plugin(InputPlugin)
    .add_plugin(AssetPlugin)
    .add_plugin(RenderPlugin)
    .add_plugin(GamePlugin)
    .run();
```

## 3. Dependencies must point inward

Subsystems may depend on core.

Core should not depend on subsystems.

Good:

```text
render -> core
physics -> core
game -> core
editor -> core + selected subsystems
```

Bad:

```text
core -> render
core -> physics
core -> editor
```

## 4. Avoid hidden coupling

A system should not assume another plugin exists unless that dependency is declared.

Bad:

```text
PhysicsPlugin silently requires TransformPlugin
```

Better:

```text
PhysicsPlugin declares dependency on TransformPlugin
```

or the required transform functionality lives in core if it is truly universal.

## 5. Prefer data boundaries over object boundaries

Do not design the engine around deep inheritance trees.

Avoid:

```text
GameObject
  Actor
    Character
      Enemy
```

Prefer:

```text
Entity + components
```

## 6. Use events for notifications, not ownership

Events are good for “something happened”.

They are bad as the primary storage of truth.

Good:

```text
CollisionEvent happened this frame
```

Bad:

```text
PositionChangedEvent is the only way to know where entities are
```

State belongs in components/resources. Events describe transitions or notifications.

## 7. Asset references should be handle-based

Runtime code should not depend directly on file paths.

Good:

```rust
MeshRenderer {
    mesh: Handle<Mesh>,
    material: Handle<Material>,
}
```

Bad:

```rust
MeshRenderer {
    mesh_path: String,
    texture_path: String,
}
```

## 8. Editor functionality should not pollute runtime core

The editor may need reflection, serialization, undo/redo, selection, gizmos, and inspectors.

These should be layered on top of the runtime architecture, not baked into every runtime type by default.

## 9. Abstract only where replacement is likely

Useful abstraction seams:

- renderer backend
- filesystem
- window/input backend
- audio backend
- asset storage

Avoid abstracting everything preemptively.

## 10. Every module must have a clear reason to exist

A module should either provide:

- a core engine primitive
- a subsystem plugin
- a backend implementation
- a game-facing API
- editor/tooling support

If a module only exists because an architecture pattern suggested it, remove or merge it.

## 11. Simulation must not depend on rendering

Simulation systems should be able to run without a renderer.

Good:

```text
movement system updates Transform
render extraction reads Transform later if rendering is enabled
```

Bad:

```text
movement system directly depends on renderer state
```

Headless execution should not require stubbing half the engine.

## 12. Deterministic state must be separated from presentation state

Simulation-relevant state should be clearly separated from renderer/editor-only state.

Examples of deterministic state:

- transforms used by gameplay
- health
- inventory
- simulation RNG state
- physics state
- world resources

Examples of non-deterministic or presentation state:

- GPU buffers
- editor selection
- debug UI open/closed state
- frame interpolation cache
- temporary render resources

Only deterministic state should participate in world hashing and replay validation.

## 13. Runtime entity IDs must not be persisted

Runtime entity IDs are allowed to be fast, compact, and temporary.

Saved data must use stable IDs.

Do not serialize raw runtime entity IDs as long-term references.

Use persistent IDs for:

- saves
- scenes
- prefabs
- replay references
- migrations

## 14. Benchmarks must be reproducible

Benchmark scenarios should define:

- seed
- tick count
- initial world/scenario
- enabled plugins
- simulation settings
- expected validation behavior, if any

Benchmark output should include enough metadata to reproduce the run.

## 15. Save migrations must be explicit

Save compatibility should not rely on accidental deserialization behavior.

When saved component schemas change, migrations should be explicit and testable.

A migration should state:

- source version
- target version
- affected type/component/resource
- transformation logic
- fallback/default behavior