Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[codespell]
skip = *.json,*.bin,*.lock,./target,./.git,./jemalloc,./node_modules,./pkg,./logs
ignore-words-list = aetheris,bevy,serde,quinn,wasm,paseto,p3,p1,p0,otlp,promql,logql,statics,unsecure,crate,lod,implementors,implementors,implementers,implementer,crate,lod,implementor,implementors,implementer,implementers,lode,ore
quiet-level = 2
check-filenames =
check-hidden =
208 changes: 208 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
<!--
Live document — update this file whenever:
• a new ECS component is added or renamed
• a new gameplay system is specified or implemented
• the core game loop changes
• ship class stats or weapon specs are balanced
• phase scope changes (P1 → P2 → P3+)
• a new engine feature is required by gameplay
Version history is tracked via git. Bump the Version field on every edit.
-->

---
Version: 1.0.0
Last Updated: 2026-04-19
Phase: P1 specification / P1 implementation in progress
Engine Dependency: aetheris-engine 0.3.0 / aetheris-protocol 0.2.4
GDD Reference: docs/VOID_RUSH_GDD.md
---

# Copilot Instructions — void-rush

Void Rush is the **flagship validation game** for the Aetheris Engine. It is a top-down
3D browser-based space MMO (mining, combat, trading) that proves every engine subsystem
works end-to-end under real gameplay load. This repository is **documentation-only** in
Phase 1; implementation lives in `aetheris-engine` and `aetheris-client`.

> **Rule**: Every game feature in this repository must trace back to an engine subsystem
> it exercises. No feature is added unless it validates something in the engine.

---

## Repository Layout

```
docs/
VOID_RUSH_GDD.md # Master Game Design Document — single source of truth
ECS_DESIGN.md # 25+ ECS components, system architecture
PLATFORM_DESIGN.md # Browser targets, WebTransport, WASM constraints
THEME_WORLD_DESIGN.md # Art direction, UI design tokens, world aesthetic
README.md # Project overview and engine validation rationale
```

---

## Core Gameplay Loop

```
Mine asteroids → Collect ore into cargo hold → Travel to Station
↑ ↓
Respawn Sell ore for credits
at Station ↓
↑ Buy ship upgrades
Die in PvP ← Attack other haulers ← Equip stronger weapons
```

**The risk-reward mechanic**: Ore increases ship mass
($a_{eff} = F / (m_{hull} + m_{ore} \times n_{ore})$),
making loaded ships slower and easier to attack. This is the core tension.

---

## Engine Systems Map

Every gameplay feature is an explicit test of an engine capability:

| Gameplay System | Engine Feature Validated | Phase |
|---|---|---|
| Ship movement + inertia | 60 Hz tick budget, Newtonian physics | P1 |
| Cargo mass penalty | Variable-mass CSP reconciliation | P1 |
| 3 ship classes | `WorldState::spawn_kind`, component schema | P1 |
| PvP weapons (4 types) | Ballistic event replication (not entity) | P1 |
| Client-side prediction | `InputCommand` pipeline + rollback | P1 |
| Safe Zone enforcement | Server-authoritative spatial rules | P1 |
| Spatial AoI | Spatial hash grid, 4-filter pipeline | P1 |
| Priority channels P0–P5 | `ChannelRegistry`, congestion shedding | P1 |
| Ore → Credits economy | `EconomyService` gRPC, idempotency keys | P1 |
| 1,000 concurrent players | tick p99 ≤ 16.6 ms gate criterion | P1 |
| Merkle chain anti-cheat | `MerkleChainState`, `SuspicionScore` | P1 |
| Sector instancing + jump gates | `RoomAndInstance`, entity budget (6,700) | P1 |
| Player crafting + auction house | Inventory gRPC, event-sourced economy | P2 |
| Multi-crew capital ships | Federation, cross-sector entity ownership | P3+ |

---

## Ship Classes

Three asymmetric classes create natural counters:

| Class | Entity Type | HP | Accel | Cargo | Role |
|---|---|---|---|---|---|
| **Interceptor** | `1` | Low | High | None | Scout / Assassin |
| **Dreadnought** | `2` | High | Low | None | Tank / Blocker |
| **Hauler** | `3` | Medium | Medium | Large | Trader (high-value target) |

**Counter triangle**: Interceptor beats Hauler (speed), Dreadnought beats Interceptor (HP), Hauler beats Dreadnought (cargo value — no reason to fight).

In ECS terms, each ship is a Bevy entity with:
- `Networked(NetworkId)` — marks it as network-replicated
- `Ownership(ClientId)` — prevents spoofed input from other clients
- `Transform` (`ComponentKind(1)`) — position/rotation, sent every tick
- `ShipStats`, `Velocity`, `CargoHold`, `Wallet`, `Health` — gameplay components

---

## ECS Component Schema (Phase 1)

```rust
// Core spatial (replicated via ComponentKind(1))
Transform { x, y, z, rotation, entity_type }
Velocity { dx, dy, dz }

// Physics
ShipStats { hull_hp: u16, shield_hp: u16, energy: f32, max_accel: f32, mass: f32 }
Loadout { chassis: u8, weapon: u8, engine: u8, shield: u8 }

// Economy
CargoHold { slots: [OreStack; 8], total_mass: f32 }
Wallet { credits: i64 } // integer cents, NEVER f32

// Combat
Health { current: u16, max: u16 }
DamageState { shield_remaining: u16, hull_remaining: u16, invuln_ticks: u8 }

// Network identity (required on every networked entity)
Networked(NetworkId)
Ownership(ClientId)

// AI
AiState { patrol_target: Option<NetworkId>, aggro_target: Option<NetworkId> }
```

> **`ComponentKind` registry**: `1` is reserved for `Transform`. All new component
> kinds start from `2`. Each kind requires a registered `BoxedReplicator` in
> `BevyWorldAdapter`.

---

## Weapon Systems

```
Pulse Laser — hitscan, instant hit, energy cost per shot
Beam Laser — channeled, continuous energy drain, DPS while held
Mining Laser — hitscan, extracts ore from asteroids (PvE only, 0 PvP damage)
Seeker Missile — homing projectile, tracks target NetworkId, 3s TTL
```

**Ballistic replication rule**: Projectiles are **NOT** replicated as ECS entities.
The server broadcasts a `BallisticEvent` (fired, hit, miss). Clients render locally
using the known positions + velocity vectors. This avoids ~200 extra entity slots per
sector in heavy combat.

```
Server: fires event → NetworkEvent::ReliableMessage { BallisticEvent::LaserFired { ... } }
Client: receives event → renders beam/missile locally → no entity in SAB
```

---

## Sector Architecture

| Zone | Description | Damage | Respawn |
|---|---|---|---|
| **Safe Zone** | Station radius (~500 m) | Nullified | Yes |
| **Contested** | Inner ring | Normal | Nearest station |
| **Lawless** | Outer ring | Normal + NPC drones | Nearest station |

- Hard cap: **6,700 entities per sector** (SAB limit: 8,192 × 80% safety margin)
- Jump gates transition players between sectors (no seamless travel in P1)
- Each sector is a separate server process; no entity state crosses sector boundary in P1

---

## Priority Channels

Under network congestion, the scheduler sheds lower-priority channels first:

| Channel | Priority | Content | Rule |
|---|---|---|---|
| P0 | Critical | Player's own entity state | Never shed |
| P1 | High | Combat events (damage, death, kills) | Never shed |
| P2 | Medium | AoI neighbors (nearby ships) | Shed at heavy congestion |
| P3 | Low | Distant entities (outer AoI ring) | Shed first |
| P4 | Background | Asteroids, loot, environment | Shed freely |
| P5 | Cosmetic | Emotes, effects | Shed freely |

---

## Performance Contracts (Phase 1 Gate Criteria)

| Metric | Target |
|---|---|
| Server tick p99 | ≤ 16.6 ms |
| Concurrent players / sector | ≥ 100 (gate), 1,000 (stretch) |
| Client FPS (Chrome/Firefox) | ≥ 30 fps |
| Entity budget / sector | ≤ 6,700 |
| Auth round-trip | ≤ 500 ms |
| Economy transaction | ≤ 200 ms p99 |

---

## Key Design Principles

- **Server is authoritative.** Clients predict locally; server always wins on conflicts.
- **Integer money.** `Wallet.credits` is always `i64` (integer cents). Never `f32`.
- **Ballistic events, not entities.** Projectiles are events, not ECS entities.
- **Safe Zones are server-enforced.** Client cannot bypass damage nullification.
- **Ownership gates every update.** An entity without `Ownership(ClientId)` rejects all client mutations.
- **No speculative features.** Every game system must map to a row in the engine systems table above.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
docs-audit:
name: Documentation Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install codespell
run: sudo apt-get update && sudo apt-get install -y codespell
- name: Run Frontmatter Lint
run: python3 scripts/doc_lint.py
- name: Run Link Check
run: python3 scripts/check_links.py
- name: Run Spelling Check
run: codespell
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ A top-down 3D space MMO built on the Aetheris engine — Newtonian physics, cont

> **[Read the Game Design Document](docs/VOID_RUSH_GDD.md)** — ship classes, weapons, economy loops, and world architecture.
>
> 🚀 **Latest Milestone:** **Wireframe Debug Renderer (M1011) functional!** Replaced placeholder overlays with a high-performance in-canvas debug pass.
> 🚀 **Latest Milestone:** **M10146** — Multirepo Consolidation & Hardening (Completed)

[![CI](https://github.com/garnizeh-labs/void-rush/actions/workflows/ci.yml/badge.svg)](https://github.com/garnizeh-labs/void-rush/actions/workflows/ci.yml)
[![Build Status](https://github.com/garnizeh-labs/void-rush/actions/workflows/ci.yml/badge.svg)](https://github.com/garnizeh-labs/void-rush/actions)
[![Rust Version](https://img.shields.io/badge/rust-1.94%2B-blue.svg?logo=rust)](https://www.rust-lang.org/)
[![License: CC BY 4.0](https://img.shields.io/badge/License-CC%20BY%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)

Expand Down
12 changes: 6 additions & 6 deletions docs/ECS_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ This document covers:
## 2. ECS in the Five-Stage Pipeline

The ECS participates in three of the five tick stages. Its budget is fixed and enforced by telemetry
(see [LC-0100](../roadmap/archive/phase-0-foundation/specs/LC-0100_tick_pipeline.md) for the full timing contract).
(see LC-0100 (Internal Timing Contract) for the full timing contract).

```
┌──────────────────────────────────────────────────────────────────┐
Expand Down Expand Up @@ -132,7 +132,7 @@ BevyWorldAdapter {

The `bimap` is the most critical field. It is a strict bijection between the protocol's `NetworkId`
and Bevy's internal `Entity` (which embeds both an index and a generation counter). Invariants B1
through B4 from [LC-0400](../roadmap/archive/phase-0-foundation/specs/LC-0400_worldstate_ecs_contract.md) govern this
through B4 from LC-0400 (Internal ECS Contract) govern this
structure: bijection, atomicity, immutability after insertion, and no ID recycling.

### 3.3 Component Registry — `ComponentReplicator`
Expand Down Expand Up @@ -309,7 +309,7 @@ still single-threaded and returns when all systems have completed.
The migration is designed to be a drop-in swap. The checklist:

1. `aetheris-ecs-custom` implements `WorldState` with the **identical method signatures**.
2. All invariants from [LC-0400](../roadmap/archive/phase-0-foundation/specs/LC-0400_worldstate_ecs_contract.md) remain
2. All invariants from LC-0400 (Internal ECS Contract) remain
satisfied: bimap bijection, atomic spawn/despawn, no NetworkId recycling.
3. The `LocalId` wrapping contract changes: P3 uses `row_index: u32 || generation: u32` packed
into a `u64` (see §7.2). The `to_bits()` / `from_bits()` round-trip contract is preserved.
Expand All @@ -324,7 +324,7 @@ The migration is designed to be a drop-in swap. The checklist:

The bridge between the boiling-hot simulation and the cold persistence tier is handled via asynchronous, lock-free channels to ensure I/O latency never blocks the tick budget.

See [PERSISTENCE_DESIGN.md](PERSISTENCE_DESIGN.md) for the full architectural specification of the persistence bridge, micro-batching strategy, and the event ledger schema.
See [PERSISTENCE_DESIGN.md](https://github.com/garnizeh-labs/aetheris-engine/blob/main/docs/PERSISTENCE_DESIGN.md) for the full architectural specification of the persistence bridge, micro-batching strategy, and the event ledger schema.

### 5.2 Event Sourcing — Append-Only Model

Expand Down Expand Up @@ -503,7 +503,7 @@ anomalous patterns are detected (impossible velocities, boundary exploits, stati
against peer entities) and decays exponentially during clean ticks.

> **Canonical definition:** The authoritative score ranges, increment values, and decay policy
> are defined in [SECURITY_DESIGN.md §8](SECURITY_DESIGN.md#8-suspicionscore-system).
> are defined in [SECURITY_DESIGN.md §8](https://github.com/garnizeh-labs/aetheris-engine/blob/main/docs/SECURITY_DESIGN.md#8-suspicionscore-system).
> The table below is a summary; SECURITY_DESIGN is the source of truth.

| Suspicion Level | Score Range | Audit Frequency | Behavior |
Expand All @@ -530,7 +530,7 @@ This is 0.3% of the 16.6ms tick budget — negligible.

Integrity verification is performed asynchronously by the Audit Worker.

See [AUDIT_DESIGN.md](https://github.com/garnizeh-labs/nexus/blob/main/docs/AUDIT_DESIGN.md) for the actor-based architecture of the audit system and its operational constraints.
See internal audit documentation for the actor-based architecture of the audit system and its operational constraints.

---

Expand Down
4 changes: 2 additions & 2 deletions docs/THEME_WORLD_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Switching Worlds is analogous to switching between entirely different apps. You
|------------|-------------|-----------------|
| `aetheris-playground` | Engine sandbox / developer tool | Tech-glass dark |
| `void-rush` | Space MMO (Aetheris: Void Rush) | Sci-fi glassmorphic dark |
| `nexus-hub` | (Future) social/hub layer | Minimal frosted light |
| `aetheris-hub` | (Future) social/hub layer | Minimal frosted light |
| `forge` | (Future) level / world editor | IDE-inspired dark |

### 1.2 What is a Theme?
Expand Down Expand Up @@ -105,7 +105,7 @@ Aetheris Engine
│ ├── Theme: blueprint (default, current aesthetic)
│ └── Theme: blueprint-lite (lower blur, accessibility)
└── World: nexus-hub (future)
└── World: aetheris-hub (future)
├── Theme: frost (default, light frosted)
└── Theme: night (dark mode)
```
Expand Down
11 changes: 11 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Void-Rush Validation Scripts

# Run all checks
check: check-docs

# Standard documentation validation
check-docs:
python3 scripts/doc_lint.py
python3 scripts/check_links.py
python3 scripts/check_branding.py
codespell --config .codespellrc || true
53 changes: 53 additions & 0 deletions scripts/check_branding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
import os
import sys
import argparse

# Configurações
FORBIDDEN_WORDS = ["nexus"]
IGNORE_DIRS = [".git", "node_modules", "target", "logs", "dist", "pkg"]
IGNORE_FILES = ["check_branding.py"] # Don't check yourself

def check_branding(root_dir):
found_violations = 0

for root, dirs, files in os.walk(root_dir):
# Filtrar diretórios ignorados
dirs[:] = [d for d in dirs if d not in IGNORE_DIRS]

for file in files:
if file in IGNORE_FILES:
continue

file_path = os.path.join(root, file)

# Pular arquivos binários básicos
if file_path.endswith((".wasm", ".png", ".jpg", ".jpeg", ".ico", ".aeb")):
continue

try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read().lower()
for word in FORBIDDEN_WORDS:
if word in content:
print(f"❌ Violation found: '{word}' in {file_path}")
found_violations += 1
except (UnicodeDecodeError, PermissionError):
# Skip files that cannot be read as text
continue

return found_violations

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Aetheris Branding Guard — Check for forbidden internal terms.")
parser.add_argument("path", nargs="?", default=".", help="Root directory to scan (default: current)")
args = parser.parse_args()

violations = check_branding(args.path)

if violations > 0:
print(f"\n🚨 Total branding violations: {violations}")
sys.exit(1)
else:
print("✅ Branding check passed. No internal terms found.")
sys.exit(0)
Loading
Loading