The workspaceDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Quiet-Wolfe/Rustic-Engine/llms.txt
Use this file to discover all available pages before exploring further.
Cargo.toml declares seven members. Six are domain crates with single responsibilities; one (rustanimate) is a support library for animation. Use the sections below to understand what belongs in each crate and what does not.
Cargo.toml
rustic-core — data types and parsing
rustic-core — data types and parsing
rustic-core is the foundation of the entire workspace. It contains every data type and parser the engine needs to understand game content. It has zero rendering or audio dependencies — if you add wgpu or kira as a dependency here, that is a bug.Modules (from crates/rustic-core/src/lib.rs):- Chart parsing (Psych Engine JSON format:
song.notes[],song.events[],mustHitSection, mid-song BPM changes) - Character JSON parsing (atlas prefix mappings, offsets, camera offsets, sing duration, health icon)
- Stage JSON parsing (sprite layers, scroll factors, positions, zoom)
- Note type definitions (4 lanes: left/down/up/right; opponent + player strums)
- Hit window constants — Sick (45 ms), Good (90 ms), Bad (135 ms), Shit (166 ms) — matching Psych Engine exactly
- Conductor: maps song position in milliseconds to beats, steps, and sections; supports mid-song BPM changes
- Scoring and rating math
- Asset path resolution and mod directory lookup
wgpu, winit, kira, or any rendering/audio crate.Testing: You can run rustic-core tests without a display or audio device, which is the point:rustic-audio — audio playback and conductor sync
rustic-audio — audio playback and conductor sync
rustic-audio owns everything related to sound. It wraps the kira audio library (version 0.9, with the ogg feature) and exposes a clean AudioEngine type that the rest of the workspace can pass around.Responsibilities:- Music (instrumental) and vocals playback and sync
- Sound effects (miss sounds, countdown bleeps)
AudioEngine— the shared handle that screens pass to each other during transitions so menu music (freakyMenu) continues playing across screen changes
kira = { version = "0.9", features = ["ogg"] }What does not belong here: gameplay logic, rendering, or timing derived from wall-clock time. All timing must come from the conductor in rustic-core, which tracks song position in milliseconds.rustic-render — all drawing
rustic-render — all drawing
rustic-render is the only crate that talks to the GPU. Nothing outside this crate should touch wgpu directly.Modules (from crates/rustic-render/src/lib.rs):GpuState— the central wgpu context (device, queue, swap chain). Every screen receives a&mut GpuStateondraw()- Sparrow XML atlas parsing: each
<SubTexture>element carries a name, position, frame offsets, and optional rotation; animation names are derived by stripping trailing digits from subtexture names - Sprite animation playback
- Dual camera system: one camera for the game world, one for the HUD overlay. Cameras follow the current singer with lerped movement; zoom pulses on beats
- Text rendering via
glyphon - Post-processing
wgpu = "28", naga = { version = "28", features = ["glsl-in", "wgsl-out"] }, winit = "0.30", glyphon = "0.10", image = "0.25", bytemuck, rustanimateWhat does not belong here: game rules, input handling, audio, or Lua scripting.The camera zoom math is intentionally improved over Psych Engine’s implementation. Psych Engine’s beat-bump is jittery;
rustic-render smooths the zoom curve while keeping the same Lua API surface so scripts remain compatible.rustic-gameplay — game logic
rustic-gameplay — game logic
rustic-gameplay implements the rules of Friday Night Funkin’ without touching the screen. It never calls into rustic-render directly — instead it emits events that the render layer consumes.Modules (from crates/rustic-gameplay/src/lib.rs):- Input handling: detecting key presses on the 4-lane strum layout
- Note hit/miss detection against Psych Engine’s exact hit windows (Sick 45 ms, Good 90 ms, Bad 135 ms, Shit 166 ms)
- Hold note logic: score per tick while held; releasing early counts as missing the remainder
- Health tracking
- Score and combo accumulation
PlayState: the top-level game loop that runs while a song is playing- Event emission so
rustic-appandrustic-rendercan react to gameplay changes
wgpu, winit drawing primitives, or kira. Render and audio react to events, not to direct calls from gameplay.rustic-scripting — Lua VM integration
rustic-scripting — Lua VM integration
rustic-scripting embeds a Lua VM and exposes the Psych Engine Lua API. The goal is that an existing Psych Engine mod script runs without modification.Public surface (from crates/rustic-scripting/src/lib.rs):ScriptManager manages all Lua scripts active for a song (stage script + per-song scripts) and owns the shared ScriptState that Lua functions read and write.Supported callback groups (matching Psych Engine’s API exactly):| Group | Callbacks |
|---|---|
| Song lifecycle | onCreate, onCreatePost, onUpdate, onUpdatePost, onSongStart, onEndSong |
| Note events | onSpawnNote, goodNoteHit, opponentNoteHit, noteMiss, noteMissPress |
| Beat/step | onBeatHit, onStepHit, onSectionHit |
| Custom events | onEvent, eventEarlyTrigger |
| Tweens/timers | onTweenCompleted, onTimerCompleted |
ScriptManager methods:TweenManager drives property animation on Lua sprites, strum props, cameras, and custom variables. Completed tweens fire onTweenCompleted; completed timers fire onTimerCompleted.What does not belong here: gameplay rules, rendering, audio playback. Scripts emit property writes that rustic-app applies to the correct subsystems — rustic-scripting itself does not hold references to the GPU or audio engine.rustic-app — binary entry point and state machine
rustic-app — binary entry point and state machine
rustic-app is the only binary in the workspace. Its job is to start a winit event loop, create the GpuState, and drive the screen state machine. It wires every other crate together without containing domain logic itself.Key types (from crates/rustic-app/src/):App struct implements winit::application::ApplicationHandler. On each RedrawRequested event it:- Computes
dt(delta time in seconds since last frame) - Calls
current_screen.update(dt) - Checks
current_screen.next_screen()— ifSome, transitions to the new screen, passing the audio engine viatake_audio/set_audio - Calls
current_screen.draw(gpu)
GAME_W = 1280.0, GAME_H = 720.0).Screens (from crates/rustic-app/src/screens/mod.rs):rustic-app needs game data it calls into the appropriate domain crate.rustanimate — animation support
rustanimate — animation support
rustanimate is a support library for animation. It is used by rustic-render to drive sprite frame sequences.It is declared as a workspace member and a workspace dependency:rustic-render.