Skip to content

Commit 3e8199b

Browse files
committed
Add mlog binary
1 parent c43af39 commit 3e8199b

File tree

5 files changed

+171
-3
lines changed

5 files changed

+171
-3
lines changed

.cargo/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
rustflags = ["-C", "target-cpu=native"]
33

44
[alias]
5+
mlog = "run --release --features mlog --bin mlog --"
56
mlogv32 = "run --release --features mlogv32 --bin mlogv32 --"

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ name = "mindustry-rs"
33
version = "0.1.0"
44
edition = "2024"
55

6+
[[bin]]
7+
name = "mlog"
8+
required-features = ["mlog"]
9+
610
[[bin]]
711
name = "mlogv32"
812
required-features = ["mlogv32"]
@@ -12,6 +16,7 @@ base64 = "0.22.1"
1216
binrw = "0.15.0"
1317
cesu8 = "1.1.0"
1418
csv = "1.3.1"
19+
enum_dispatch = "0.3.13"
1520
flate2 = { version = "1.1.2", features = ["zlib-rs"], default-features = false }
1621
indexmap = "2.10.0"
1722
itertools = "0.14.0"
@@ -35,7 +40,7 @@ clap = { version = "4.5.42", features = ["derive"], optional = true }
3540
crossterm = { version = "0.29.0", optional = true }
3641
cursive = { version = "0.21.1", optional = true }
3742
indicatif = { version = "0.18.0", optional = true }
38-
enum_dispatch = "0.3.13"
43+
clap-stdin = { version = "0.6.0", optional = true }
3944

4045
[build-dependencies]
4146
lalrpop = "0.22.2"
@@ -44,8 +49,12 @@ lalrpop = "0.22.2"
4449
pretty_assertions = "1.4.1"
4550

4651
[features]
47-
mlogv32 = [
52+
mlog = [
4853
"dep:clap",
54+
"dep:clap-stdin",
55+
]
56+
mlogv32 = [
57+
"mlog",
4958
"dep:crossterm",
5059
"dep:cursive",
5160
"dep:indicatif",

src/bin/mlog.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use std::{error::Error, time::Instant};
2+
3+
use clap::Parser;
4+
use clap_stdin::FileOrStdin;
5+
use mindustry_rs::{
6+
logic::vm::{
7+
Building, BuildingData, HYPER_PROCESSOR, LOGIC_PROCESSOR, LogicVMBuilder, MEMORY_BANK,
8+
MEMORY_CELL, MESSAGE, MICRO_PROCESSOR, WORLD_PROCESSOR,
9+
},
10+
types::{Object, Point2, ProcessorConfig, ProcessorLinkConfig},
11+
};
12+
use strum_macros::EnumString;
13+
use widestring::U16String;
14+
15+
#[derive(Parser)]
16+
#[command(version)]
17+
struct Cli {
18+
/// Mlog code to load and run
19+
code: FileOrStdin,
20+
21+
/// Processor type to use (micro, logic, hyper, world)
22+
#[arg(long, short, default_value_t = ProcessorType::World)]
23+
processor: ProcessorType,
24+
25+
/// Simulated time delta (0.5 = 120 fps, 1 = 60 fps, 2 = 30 fps)
26+
#[arg(long, default_value_t = 1.0, value_parser = time_delta_parser)]
27+
delta: f64,
28+
29+
/// Maximum number of ticks to run the simulation for
30+
#[arg(long)]
31+
max_ticks: Option<u32>,
32+
}
33+
34+
fn time_delta_parser(s: &str) -> Result<f64, String> {
35+
match s.parse() {
36+
Ok(value) if value > 0. && value <= 6. => Ok(value),
37+
Ok(_) => Err(format!("{s} is not in range (0, 6]")),
38+
Err(_) => Err(format!("{s} is not a valid number")),
39+
}
40+
}
41+
42+
#[allow(clippy::enum_variant_names)]
43+
#[derive(Debug, Clone, Copy, EnumString, strum_macros::Display)]
44+
#[strum(serialize_all = "lowercase")]
45+
enum ProcessorType {
46+
Micro,
47+
Logic,
48+
Hyper,
49+
World,
50+
}
51+
52+
impl ProcessorType {
53+
fn name(&self) -> &str {
54+
match self {
55+
Self::Micro => MICRO_PROCESSOR,
56+
Self::Logic => LOGIC_PROCESSOR,
57+
Self::Hyper => HYPER_PROCESSOR,
58+
Self::World => WORLD_PROCESSOR,
59+
}
60+
}
61+
}
62+
63+
fn main() -> Result<(), Box<dyn Error>> {
64+
let cli = Cli::parse();
65+
66+
let mut builder = LogicVMBuilder::new();
67+
builder.add_buildings([
68+
Building::from_processor_config(
69+
cli.processor.name(),
70+
Point2::new(0, 0),
71+
&ProcessorConfig {
72+
code: cli.code.contents()?,
73+
links: vec![
74+
ProcessorLinkConfig::unnamed(3, 0),
75+
ProcessorLinkConfig::unnamed(4, 0),
76+
ProcessorLinkConfig::unnamed(5, 0),
77+
],
78+
},
79+
&builder,
80+
)?,
81+
Building::from_config(MESSAGE, Point2::new(3, 0), &Object::Null, &builder)?,
82+
Building::from_config(MEMORY_CELL, Point2::new(4, 0), &Object::Null, &builder)?,
83+
Building::from_config(MEMORY_BANK, Point2::new(5, 0), &Object::Null, &builder)?,
84+
]);
85+
let vm = builder.build()?;
86+
87+
let processor = vm.building((0, 0).into()).unwrap();
88+
assert_eq!(processor.block.name.as_str(), cli.processor.name());
89+
90+
let message = vm.building((3, 0).into()).unwrap();
91+
assert_eq!(message.block.name.as_str(), MESSAGE);
92+
93+
let start = Instant::now();
94+
let mut ticks = 0u32;
95+
let mut prev_message = U16String::new();
96+
97+
let all_stopped = loop {
98+
vm.do_tick_with_delta(start.elapsed(), cli.delta);
99+
ticks += 1;
100+
101+
if let BuildingData::Message(message) = &*message.data.borrow()
102+
&& !message.is_empty()
103+
&& *message != prev_message
104+
{
105+
println!("{}", message.display());
106+
prev_message = message.clone();
107+
}
108+
109+
if vm.running_processors() == 0 {
110+
break true;
111+
}
112+
113+
if let Some(max_ticks) = cli.max_ticks
114+
&& ticks >= max_ticks
115+
{
116+
break false;
117+
}
118+
};
119+
120+
if let BuildingData::Processor(processor) = &*processor.data.borrow()
121+
&& !processor.state.printbuffer.is_empty()
122+
{
123+
println!("{}", processor.state.printbuffer.display());
124+
}
125+
126+
if all_stopped {
127+
println!("--------\nAll processors stopped, halting.");
128+
} else {
129+
println!("--------\nTick limit reached, halting.");
130+
}
131+
132+
let time = start.elapsed();
133+
println!("Runtime: {time:?}");
134+
println!("Ticks completed: {ticks}");
135+
println!("Average time per tick: {:?}", time / ticks);
136+
println!(
137+
"Average ticks per second: {:.1}",
138+
(ticks as f64) / time.as_secs_f64()
139+
);
140+
141+
Ok(())
142+
}

src/logic/vm/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,17 @@ impl LogicVM {
6161
/// Run the simulation until all processors halt, or until a number of ticks are finished.
6262
/// Returns true if all processors halted, or false if the tick limit was reached.
6363
pub fn run(&self, max_ticks: Option<usize>) -> bool {
64+
self.run_with_delta(max_ticks, 1.0)
65+
}
66+
67+
/// Run the simulation until all processors halt, or until a number of ticks are finished.
68+
/// Returns true if all processors halted, or false if the tick limit was reached.
69+
pub fn run_with_delta(&self, max_ticks: Option<usize>, delta: f64) -> bool {
6470
let start = Instant::now();
6571
let mut tick = 0;
6672

6773
loop {
68-
self.do_tick(start.elapsed());
74+
self.do_tick_with_delta(start.elapsed(), delta);
6975

7076
if self.running_processors.get() == 0 {
7177
// all processors finished, return true

0 commit comments

Comments
 (0)