Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[workspace]
members = [
"crates/sim-core",
"crates/sim-proto",
"crates/sim-server",
"crates/sim-cli",
"crates/sim-ui",
]
resolver = "2"

[workspace.package]
edition = "2021"
license = "MIT"
version = "0.1.0"

[workspace.dependencies]
sim-core = { path = "crates/sim-core" }
sim-proto = { path = "crates/sim-proto" }
sim-server = { path = "crates/sim-server" }
sim-ui = { path = "crates/sim-ui" }
15 changes: 15 additions & 0 deletions crates/sim-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "sim-cli"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
sim-core.workspace = true
sim-proto.workspace = true
sim-server.workspace = true
sim-ui = { workspace = true, optional = true }

[features]
default = []
ui = ["dep:sim-ui"]
24 changes: 24 additions & 0 deletions crates/sim-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use sim_proto::SimulationRequest;
use sim_server::SimulationService;

pub type CliResult<T> = Result<T, Box<dyn std::error::Error>>;

pub fn realtime_entrypoint(steps: u32) -> CliResult<()> {
let mut service = SimulationService::new();
for _ in 0..steps {
let _ = service.handle_step(SimulationRequest {
delta_time_seconds: 1.0 / 60.0,
})?;
}
Ok(())
}

pub fn fixed_step_entrypoint(steps: u32, dt_seconds: f32) -> CliResult<()> {
let mut service = SimulationService::new();
for _ in 0..steps {
let _ = service.handle_step(SimulationRequest {
delta_time_seconds: dt_seconds,
})?;
}
Ok(())
}
25 changes: 25 additions & 0 deletions crates/sim-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::env;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = env::args().skip(1);
let mode = args.next().unwrap_or_else(|| "realtime".to_string());

match mode.as_str() {
"realtime" => {
let steps = args.next().and_then(|v| v.parse().ok()).unwrap_or(600);
sim_cli::realtime_entrypoint(steps)
}
"fixed-step" => {
let steps = args.next().and_then(|v| v.parse().ok()).unwrap_or(600);
let dt_seconds = args
.next()
.and_then(|v| v.parse().ok())
.unwrap_or(1.0 / 120.0);
sim_cli::fixed_step_entrypoint(steps, dt_seconds)
}
_ => {
eprintln!("Usage: sim-cli [realtime [steps] | fixed-step [steps] [dt_seconds]]");
Ok(())
}
}
}
1 change: 1 addition & 0 deletions crates/sim-core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
7 changes: 7 additions & 0 deletions crates/sim-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "sim-core"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
37 changes: 37 additions & 0 deletions crates/sim-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[derive(Debug, Clone)]
pub struct WorldState {
pub tick: u64,
pub simulation_time_seconds: f32,
}

#[derive(Debug, Clone)]
pub struct Simulator {
world: WorldState,
}

impl Default for Simulator {
fn default() -> Self {
Self::new()
}
}

impl Simulator {
pub fn new() -> Self {
Self {
world: WorldState {
tick: 0,
simulation_time_seconds: 0.0,
},
}
}

pub fn step(&mut self, dt_seconds: f32) -> &WorldState {
self.world.tick += 1;
self.world.simulation_time_seconds += dt_seconds.max(0.0);
&self.world
}

pub fn world(&self) -> &WorldState {
&self.world
}
}
7 changes: 7 additions & 0 deletions crates/sim-proto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "sim-proto"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
27 changes: 27 additions & 0 deletions crates/sim-proto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#[derive(Debug, Clone)]
pub struct SimulationRequest {
pub delta_time_seconds: f32,
}

#[derive(Debug, Clone)]
pub struct SimulationResponse {
pub tick: u64,
pub simulation_time_seconds: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolError {
InvalidDeltaTime,
}

impl core::fmt::Display for ProtocolError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ProtocolError::InvalidDeltaTime => {
f.write_str("invalid request: delta_time_seconds must be finite and non-negative")
}
}
}
}

impl std::error::Error for ProtocolError {}
9 changes: 9 additions & 0 deletions crates/sim-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "sim-server"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
sim-core.workspace = true
sim-proto.workspace = true
35 changes: 35 additions & 0 deletions crates/sim-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use sim_core::Simulator;
use sim_proto::{ProtocolError, SimulationRequest, SimulationResponse};

pub struct SimulationService {
simulator: Simulator,
}

impl Default for SimulationService {
fn default() -> Self {
Self::new()
}
}

impl SimulationService {
pub fn new() -> Self {
Self {
simulator: Simulator::new(),
}
}

pub fn handle_step(
&mut self,
request: SimulationRequest,
) -> Result<SimulationResponse, ProtocolError> {
if !request.delta_time_seconds.is_finite() || request.delta_time_seconds < 0.0 {
return Err(ProtocolError::InvalidDeltaTime);
}

let world = self.simulator.step(request.delta_time_seconds);
Ok(SimulationResponse {
tick: world.tick,
simulation_time_seconds: world.simulation_time_seconds,
})
}
}
17 changes: 17 additions & 0 deletions crates/sim-ui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "sim-ui"
version.workspace = true
edition.workspace = true
license.workspace = true

[lib]
path = "src/lib.rs"

[[bin]]
name = "sim-ui"
path = "src/main.rs"
required-features = ["runtime"]

[features]
default = []
runtime = []
3 changes: 3 additions & 0 deletions crates/sim-ui/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn launch() {
// Placeholder for a future visual frontend.
}
3 changes: 3 additions & 0 deletions crates/sim-ui/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
sim_ui::launch();
}