# apps/game — Bevy/Rust Game Client Single-player space exploration RPG built with [Bevy 0.16](https://bevyengine.org). Crate name: `void-nav`. This app is **independent of the pnpm/TS workspace** — no Vite, no React, no Tailwind. It builds with plain `cargo` from this directory. ## Commands ```bash cargo run # Build + launch the game cargo check # Fast typecheck (no codegen) cargo build # Build without running cargo clippy # Lint cargo test # Run unit tests ``` ## Module Layout ``` src/ ├── main.rs # App builder: plugins, state init, system registration ├── state.rs # AppState enum (MainMenu, Galaxy, CharacterCreation, InGame, Options) ├── camera.rs # Camera spawn ├── ui/ # UI screens (menus, HUD, etc.) │ ├── mod.rs │ └── main_menu.rs └── gameplay/ # Non-UI gameplay systems ├── mod.rs ├── character_creation/ # Character creation scene (skeleton) ├── galaxy/ # Procedural galaxy inspection scene ├── movement/ # Ship movement (kinematic + orbital) ├── physics/ # Physics primitives (mass, gravity, etc.) └── star_map/ # Star map data + visualization ``` ### When to add a file vs a folder - **Default**: one file per feature (e.g. `main_menu.rs`). - **Promote to a folder** when the file exceeds ~300 lines, mixes UI + logic + data, or needs shared private helpers. - A promoted folder should expose its public API via `mod.rs` and ideally bundle its systems into a Bevy `Plugin` (see pattern below). ### Plugin pattern (recommended once a folder exists) ```rust // src/gameplay/galaxy/mod.rs pub struct GalaxyPlugin; impl Plugin for GalaxyPlugin { fn build(&self, app: &mut App) { app.add_systems(OnEnter(AppState::Galaxy), setup_galaxy) .add_systems(OnExit(AppState::Galaxy), despawn_galaxy) .add_systems(Update, ( /* update systems */ ).run_if(in_state(AppState::Galaxy))); } } ``` Then `main.rs` collapses to `app.add_plugins((..., GalaxyPlugin))`. ## Naming Conventions Follows [Rust RFC 344](https://rust-lang.github.io/api-guidelines/naming.html). Bevy layers these additional conventions: | Item | Convention | Example | |---|---|---| | Files, modules, directories | `snake_case` | `galaxy`, `main_menu` | | Crate name | `snake_case` | `void-nav` | | Structs, enums, enum variants | `UpperCamelCase` | `AppState`, `Galaxy` | | Components | `UpperCamelCase` (suffix optional) | `Player`, `MainMenuUi` | | Resources | `UpperCamelCase` | `ClearColor`, `Time` | | States | `UpperCamelCase` + `State` suffix | `AppState`, `GameState` | | Plugins | `UpperCamelCase` + `Plugin` suffix | `GalaxyPlugin` | | Functions, systems, locals | `snake_case`, verb-first | `spawn_camera`, `setup_main_menu` | | Constants, statics | `SCREAMING_SNAKE_CASE` | `MAX_HEALTH` | ## Warnings & Errors Policy **Never suppress compiler warnings or errors.** This includes but is not limited to: - Do **not** add `#[allow(...)]` / `#[allow(dead_code)]` / `#[allow(unused)]` / `#[allow(unused_variables)]` or any other lint suppression attribute. - Do **not** prefix unused variables with `_` to silence warnings (e.g. `_unused_var`). - Do **not** use `let _ = ...` to discard results that might carry meaningful errors. - Do **not** add `#[cfg_attr(..., allow(...))]` or equivalent conditional suppressions. - Do **not** use `#![allow(...)]` at the crate or module level. If a warning or error fires, **fix the underlying issue** — remove dead code, use the variable, handle the error properly, or restructure the code. Suppressing warnings hides real problems and is not an acceptable fix. ## Architecture Notes - **State machine driven**: each `AppState` variant has its own UI/systems wired via `OnEnter` / `OnExit` / `Update` (latter guarded by `in_state`). - **Persistence**: SpacetimeDB will be the authoritative backend (via the Rust SDK, not the TS bindings). Not yet wired up. - **No `module_bindings/` here**: TS bindings live in the TS workspace and are consumed by the docs prototype.