0.1.0-0-pre.3

This commit is contained in:
2026-09-16 00:46:04 +02:00
parent 8c0252e34e
commit 2c79a05dd6
26 changed files with 589 additions and 53 deletions

View File

@@ -0,0 +1,36 @@
// file: crates/engines/engine-v1-common/src/frame.rs
// version: 1
/// Immutable timing information associated with one engine update.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EngineFrame {
index: u64,
delta: std::time::Duration,
elapsed: std::time::Duration,
}
impl EngineFrame {
/// Creates timing information for one update.
#[must_use]
pub const fn new(index: u64, delta: std::time::Duration, elapsed: std::time::Duration) -> Self {
return Self { index, delta, elapsed };
}
/// Returns the zero-based update index.
#[must_use]
pub const fn index(self) -> u64 {
return self.index;
}
/// Returns the duration represented by this update.
#[must_use]
pub const fn delta(self) -> std::time::Duration {
return self.delta;
}
/// Returns the accumulated simulated duration before this update completes.
#[must_use]
pub const fn elapsed(self) -> std::time::Duration {
return self.elapsed;
}
}

View File

@@ -0,0 +1,83 @@
// file: crates/engines/engine-v1-common/src/game_loop.rs
// version: 1
/// Minimal update contract implemented by a game state consumed by engine V1.
pub trait EngineGame {
/// Advances the game by one engine update using platform-independent input.
fn update(&mut self, frame: crate::EngineFrame, input: crate::InputState);
}
/// Deterministic fixed-step driver used before and underneath platform event loops.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FixedStepRunner {
delta: std::time::Duration,
next_frame_index: u64,
elapsed: std::time::Duration,
}
impl FixedStepRunner {
/// Creates a fixed-step runner using the supplied update duration.
#[must_use]
pub const fn new(delta: std::time::Duration) -> Self {
return Self { delta, next_frame_index: 0, elapsed: std::time::Duration::ZERO };
}
/// Returns the configured fixed update duration.
#[must_use]
pub const fn delta(self) -> std::time::Duration {
return self.delta;
}
/// Returns the index that will be assigned to the next update.
#[must_use]
pub const fn next_frame_index(self) -> u64 {
return self.next_frame_index;
}
/// Returns the accumulated simulated time.
#[must_use]
pub const fn elapsed(self) -> std::time::Duration {
return self.elapsed;
}
/// Advances one game state by one deterministic update.
pub fn tick<G>(&mut self, game: &mut G, input: crate::InputState)
where
G: crate::EngineGame,
{
let frame = crate::EngineFrame::new(self.next_frame_index, self.delta, self.elapsed);
game.update(frame, input);
self.next_frame_index = self.next_frame_index.saturating_add(1);
self.elapsed = self.elapsed.saturating_add(self.delta);
return;
}
}
#[cfg(test)]
mod unit_tests {
#[derive(Default)]
struct CountingGame {
updates: u64,
last_frame: Option<crate::EngineFrame>,
}
impl crate::EngineGame for CountingGame {
fn update(&mut self, frame: crate::EngineFrame, _input: crate::InputState) {
self.updates = self.updates.saturating_add(1);
self.last_frame = Some(frame);
return;
}
}
#[test]
fn fixed_step_runner_advances_index_and_elapsed_time() {
let mut game = CountingGame::default();
let mut runner = crate::FixedStepRunner::new(std::time::Duration::from_millis(16));
runner.tick(&mut game, crate::InputState::none());
runner.tick(&mut game, crate::InputState::none());
assert_eq!(game.updates, 2);
assert_eq!(runner.next_frame_index(), 2);
assert_eq!(runner.elapsed(), std::time::Duration::from_millis(32));
assert_eq!(game.last_frame.map(crate::EngineFrame::index), Some(1));
}
}

View File

@@ -0,0 +1,61 @@
// file: crates/engines/engine-v1-common/src/input.rs
// version: 1
/// Platform-independent snapshot of logical input actions for one update.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct InputState {
left: bool,
right: bool,
up: bool,
down: bool,
primary: bool,
secondary: bool,
pause: bool,
}
impl InputState {
/// Creates a snapshot with no active action.
#[must_use]
pub const fn none() -> Self {
return Self { left: false, right: false, up: false, down: false, primary: false, secondary: false, pause: false };
}
/// Returns a copy with the selected logical action set to the requested state.
#[must_use]
pub const fn with_action(mut self, action: crate::GameAction, active: bool) -> Self {
match action {
crate::GameAction::Left => self.left = active,
crate::GameAction::Right => self.right = active,
crate::GameAction::Up => self.up = active,
crate::GameAction::Down => self.down = active,
crate::GameAction::Primary => self.primary = active,
crate::GameAction::Secondary => self.secondary = active,
crate::GameAction::Pause => self.pause = active,
}
return self;
}
/// Reports whether the selected logical action is active.
#[must_use]
pub const fn is_active(self, action: crate::GameAction) -> bool {
return match action {
crate::GameAction::Left => self.left,
crate::GameAction::Right => self.right,
crate::GameAction::Up => self.up,
crate::GameAction::Down => self.down,
crate::GameAction::Primary => self.primary,
crate::GameAction::Secondary => self.secondary,
crate::GameAction::Pause => self.pause,
};
}
}
#[cfg(test)]
mod unit_tests {
#[test]
fn action_state_is_independent() {
let state = crate::InputState::none().with_action(crate::GameAction::Primary, true);
assert!(state.is_active(crate::GameAction::Primary));
assert!(!state.is_active(crate::GameAction::Secondary));
}
}

View File

@@ -1,5 +1,5 @@
// file: crates/engines/engine-v1-common/src/lib.rs
// version: 1
// version: 2
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -8,6 +8,17 @@
//! Common, platform-independent primitives for engine generation V1.
mod action;
mod frame;
mod game_loop;
mod input;
/// Re-export of the canonical logical input action used by engine V1 games.
pub use self::action::GameAction;
/// Re-export of immutable timing information for one engine update.
pub use self::frame::EngineFrame;
/// Re-export of the minimal game update contract consumed by engine V1.
pub use self::game_loop::EngineGame;
/// Re-export of the deterministic fixed-step driver.
pub use self::game_loop::FixedStepRunner;
/// Re-export of the platform-independent logical input snapshot.
pub use self::input::InputState;