Skip to content

Commit b489ab3

Browse files
committed
WIP native command migration and UI updates
1 parent 63fd7e8 commit b489ab3

114 files changed

Lines changed: 2228 additions & 1627 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Exactly one does; the other two build nothing.
1010
| `ui-rust.json` | rust | rust | the native (Rust) UI |
1111
| `ui-chili-native-chonsole.json` | chili | rust | Chili UI, native console |
1212
| `ui-rmlui-native-chonsole.json` | rmlui | rust | Lua RmlUi UI, native console |
13+
| `ui-rust-dev.json` | rust | rust | native UI + Dev tab, debug logging |
14+
15+
A config may also carry an `env` object; its entries are set in the engine's
16+
environment (`ui-rust-dev.json` uses it for `SBC_DEV_PANEL` and `SBC_LOG_LEVEL`).
1317

1418
The old names (`rust-rmlui.json`, `mixed-rust-*`) named the *chonsole*, not the
1519
UI, so `rust-rmlui` was the Lua UI. They are renamed above.

config/ui-rust-dev.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"chonsole": "rust",
3+
"ui": "rust",
4+
"env": {
5+
"SBC_DEV_PANEL": "1",
6+
"SBC_LOG_LEVEL": "debug"
7+
}
8+
}

docs/porting/conventions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ the file reads like a newspaper (headline first, detail below).
208208
ordering meaning. The rule is only: the public command on top, helpers underneath.
209209
- Keep one `impl` block per type — don't split a type's methods across several
210210
`impl` blocks to satisfy ordering; order the methods *within* the block instead.
211+
- Within each `impl` block, put every `pub`/`pub(...)` method before its private
212+
methods. The Rust step-down lint enforces this independently for each block.
211213
- Free helper functions go below the code that calls them; a leaf used by several
212214
callers goes after the last of them.
213215

native/src/sbc/actions/clipboard.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use std::any::Any;
88

99
use spring_native::prelude::NativeInterfaceRef;
1010

11+
use crate::sbc::command_system::command::Command;
1112
use crate::sbc::command_system::model::{Model, ModelFactory};
12-
use crate::sbc::objects::ObjectKind;
13+
use crate::sbc::objects::{AddObjectCommand, ObjectKind};
1314

1415
inventory::submit! {
1516
ModelFactory { make: |_iface| Box::new(Clipboard::default()) }
@@ -44,16 +45,15 @@ impl Clipboard {
4445
}
4546
}
4647

47-
/// Build AddObjectCommand envelopes for every copied object, offset so the
48+
/// Build `AddObjectCommand`s for every copied object, offset so the
4849
/// centroid lands at `(ground_x, ground_z)`. Each object's Y maintains its
4950
/// original height-above-ground, queried live from the terrain.
50-
pub fn paste_envelopes(
51+
pub fn paste_commands(
5152
&self,
5253
interface: &NativeInterfaceRef,
5354
ground_x: f32,
5455
ground_z: f32,
55-
next: &mut u64,
56-
) -> Vec<String> {
56+
) -> Vec<Box<dyn Command>> {
5757
if self.copied.is_empty() {
5858
return vec![];
5959
}
@@ -76,7 +76,7 @@ impl Clipboard {
7676
let dz = ground_z - cz / count as f32;
7777

7878
let terrain = interface.terrain();
79-
let mut envelopes = Vec::new();
79+
let mut commands = Vec::new();
8080
for (kind, json) in &self.copied {
8181
let mut obj = json.clone();
8282
if let serde_json::Value::Object(map) = &mut obj {
@@ -95,16 +95,9 @@ impl Clipboard {
9595
pos["z"] = serde_json::json!(nz);
9696
}
9797
}
98-
envelopes.push(crate::sbc::envelope::envelope_fields(
99-
"AddObjectCommand",
100-
next,
101-
serde_json::json!({
102-
"objType": kind,
103-
"params": obj,
104-
}),
105-
));
98+
commands.push(Box::new(AddObjectCommand::new(*kind, obj)) as Box<dyn Command>);
10699
}
107-
envelopes
100+
commands
108101
}
109102
}
110103

native/src/sbc/actions/dialog.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
use spring_native::prelude::NativeInterfaceRef;
55

66
use super::paths::PROJECTS_DIR;
7+
use crate::sbc::command_system::command::Command;
78

89
/// What an action wants the manager to do.
910
pub enum ActionResult {
1011
/// Nothing to do (not executable, or handled internally like selection ops).
1112
None,
12-
/// Route these command envelopes through the command system.
13-
Commands(Vec<String>),
13+
/// Submit these typed commands directly — no JSON envelope, no `className`,
14+
/// no producer-assigned id (the command manager allocates one on execute).
15+
NativeCommands(Vec<Box<dyn Command>>),
1416
/// Open the file browser. `on_accept` is called with the picked path.
1517
OpenFileDialog {
1618
config: FileDialogConfig,
@@ -55,6 +57,6 @@ pub struct FileDialogResult {
5557
pub file_type: Option<String>,
5658
}
5759

58-
/// A type-erased callback that produces command envelopes from a dialog result.
60+
/// A type-erased callback that produces typed commands from a dialog result.
5961
pub type FileAcceptFn =
60-
Box<dyn FnOnce(&FileDialogResult, &NativeInterfaceRef, &mut u64) -> Vec<String>>;
62+
Box<dyn FnOnce(&FileDialogResult, &NativeInterfaceRef) -> Vec<Box<dyn Command>>>;

native/src/sbc/actions/project.rs

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,57 @@ use spring_native::prelude::NativeInterfaceRef;
66

77
use super::helpers::{game_id, rand_u32};
88
use super::paths::PROJECTS_DIR;
9-
use crate::sbc::envelope::envelope_fields;
9+
use crate::sbc::command_system::command::Command;
10+
use crate::sbc::project::commands::{ReloadIntoProjectCommand, SaveProjectInfoCommand};
11+
use crate::sbc::project::ProjectData;
1012

1113
/// The blank map's archive name; its size comes from `randomMapOptions`.
1214
const BLANK_MAP: &str = "SB_Blank_Map";
1315

14-
/// Build the command envelopes for creating a new project, called by the
15-
/// manager after the NewProject dialog completes.
16+
/// Build the commands for creating a new project, called by the manager after
17+
/// the NewProject dialog completes.
1618
pub fn commit_new_project(
1719
name: &str,
1820
map_name: &str,
1921
size_x: Option<f32>,
2022
size_y: Option<f32>,
2123
interface: &NativeInterfaceRef,
22-
next: &mut u64,
23-
) -> Vec<String> {
24+
) -> Vec<Box<dyn Command>> {
2425
let project_name = name.trim().trim_end_matches(".sdd");
2526
let path = format!("{PROJECTS_DIR}{project_name}.sdd");
2627
let (game_name, game_version) = game_id(interface);
2728

2829
// For the blank map, the size is carried in randomMapOptions and the engine
2930
// generates a fresh map; a seed keeps the archive out of a stale cache.
3031
let random_map_options = if map_name == BLANK_MAP {
31-
serde_json::json!({
32+
Some(serde_json::json!({
3233
"mapSeed": rand_u32(),
3334
"new_map_x": size_x.unwrap_or(10.0),
3435
"new_map_y": size_y.unwrap_or(10.0),
35-
})
36+
}))
3637
} else {
37-
serde_json::Value::Null
38+
None
3839
};
3940

4041
// The full project record, so the chosen map and target game are saved into
4142
// the start script the reload reads back.
42-
let mut project = serde_json::json!({
43-
"name": project_name,
44-
"path": path,
45-
"mapName": map_name,
46-
"game": { "name": game_name, "version": game_version },
47-
"mutators": [format!("{project_name} 1.0")],
48-
});
49-
if !random_map_options.is_null() {
50-
project["randomMapOptions"] = random_map_options;
51-
}
43+
let project = ProjectData {
44+
name: Some(project_name.to_string()),
45+
path: Some(path.clone()),
46+
map_name: Some(map_name.to_string()),
47+
game: Some(serde_json::json!({ "name": game_name, "version": game_version })),
48+
random_map_options,
49+
mutators: vec![format!("{project_name} 1.0")],
50+
};
5251

5352
vec![
54-
envelope_fields(
55-
"SaveProjectInfoCommand",
56-
next,
57-
serde_json::json!({
58-
"name": project_name,
59-
"path": path,
60-
"isNewProject": true,
61-
"project": project,
62-
}),
63-
),
64-
envelope_fields(
65-
"ReloadIntoProjectCommand",
66-
next,
67-
serde_json::json!({
68-
"path": path,
69-
"modOptions": serde_json::json!({}),
70-
"gameName": game_name,
71-
"gameVersion": game_version,
72-
}),
73-
),
53+
Box::new(SaveProjectInfoCommand::new(
54+
project_name.to_string(),
55+
path.clone(),
56+
true,
57+
Some(project),
58+
)),
59+
Box::new(ReloadIntoProjectCommand::new(path, game_name, game_version)),
7460
]
7561
}
7662

0 commit comments

Comments
 (0)