diff --git a/src/commands/delete_blocks.rs b/src/commands/delete_blocks.rs index 8a5d678..9234259 100644 --- a/src/commands/delete_blocks.rs +++ b/src/commands/delete_blocks.rs @@ -10,6 +10,7 @@ fn delete_blocks(inst: &mut InstBundle) { inst.status.begin_editing(); for key in keys { + // TODO: This is kind of inefficient seeming. inst.status.inc_done(); inst.db.delete_block(key).unwrap(); } diff --git a/src/commands/fill.rs b/src/commands/fill.rs index da8f0e0..43cb7ae 100644 --- a/src/commands/fill.rs +++ b/src/commands/fill.rs @@ -1,5 +1,6 @@ use super::Command; +use crate::unwrap_or; use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block}; use crate::instance::{ArgType, InstBundle}; use crate::map_block::{MapBlock}; @@ -32,9 +33,14 @@ fn fill(inst: &mut InstBundle) { let mut count: u64 = 0; for key in keys { + inst.status.inc_done(); + let pos = Vec3::from_block_key(key); let data = inst.db.get_block(key).unwrap(); - let mut block = MapBlock::deserialize(&data).unwrap(); + let mut block = unwrap_or!(MapBlock::deserialize(&data), { + inst.status.inc_failed(); + continue; + }); if area_contains_block(&area, pos) { let nd = block.node_data.get_mut(); @@ -57,12 +63,10 @@ fn fill(inst: &mut InstBundle) { } inst.db.set_block(key, &block.serialize()).unwrap(); - inst.status.inc_done(); } inst.status.end_editing(); - inst.status.log_info( - format!("{} nodes filled.", fmt_big_num(count)).as_str()); + inst.status.log_info(format!("{} nodes filled.", fmt_big_num(count))); } diff --git a/src/commands/replace_nodes.rs b/src/commands/replace_nodes.rs index d895685..8a4d6d6 100644 --- a/src/commands/replace_nodes.rs +++ b/src/commands/replace_nodes.rs @@ -144,8 +144,7 @@ fn replace_nodes(inst: &mut InstBundle) { // tk.print(); inst.status.end_editing(); - inst.status.log_info( - format!("{} nodes replaced.", fmt_big_num(count)).as_str()); + inst.status.log_info(format!("{} nodes replaced.", fmt_big_num(count))); } diff --git a/src/commands/set_param2.rs b/src/commands/set_param2.rs index 79ee8b5..c92ea45 100644 --- a/src/commands/set_param2.rs +++ b/src/commands/set_param2.rs @@ -96,8 +96,7 @@ fn set_param2(inst: &mut InstBundle) { } inst.status.end_editing(); - inst.status.log_info( - format!("{} nodes set.", fmt_big_num(count)).as_str()); + inst.status.log_info(format!("{} nodes set.", fmt_big_num(count))); } diff --git a/src/instance.rs b/src/instance.rs index 45ad62d..ff7e39c 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -67,6 +67,7 @@ pub struct InstStatus { pub show_progress: bool, pub blocks_total: usize, pub blocks_done: usize, + pub blocks_failed: usize, pub state: InstState } @@ -76,6 +77,7 @@ impl InstStatus { show_progress: true, blocks_total: 0, blocks_done: 0, + blocks_failed: 0, state: InstState::Ignore } } @@ -110,6 +112,10 @@ pub struct StatusServer { } impl StatusServer { + pub fn get_status(&self) -> InstStatus { + self.status.lock().unwrap().clone() + } + pub fn set_state(&self, new_state: InstState) { self.status.lock().unwrap().state = new_state; self.event_tx.send(InstEvent::NewState(new_state)).unwrap(); @@ -123,6 +129,11 @@ impl StatusServer { self.status.lock().unwrap().blocks_done += 1; } + pub fn inc_failed(&mut self) { + // TODO: Proper error handling for all commands. + self.status.lock().unwrap().blocks_failed += 1; + } + pub fn set_show_progress(&self, sp: bool) { self.status.lock().unwrap().show_progress = sp; } @@ -182,44 +193,59 @@ fn status_channel() -> (StatusServer, StatusClient) { fn verify_args(args: &InstArgs) -> anyhow::Result<()> { - fn verify_item_name(name: &str) -> anyhow::Result<()> { + fn is_valid_name(name: &str) -> bool { if name == "air" || name == "ignore" { - Ok(()) + true } else { - let delim = name.find(':') - .ok_or(anyhow::anyhow!(""))?; + let delim = match name.find(':') { + Some(d) => d, + None => return false + }; let mod_name = &name[..delim]; - anyhow::ensure!(mod_name.find(|c: char| - !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') - ).is_none()); - let item_name = &name[delim + 1..]; - anyhow::ensure!(item_name.find(|c: char| - !(c.is_ascii_alphanumeric() || c == '_') - ).is_none()); - Ok(()) + if mod_name.find(|c: char| + !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')) + .is_some() + || item_name.find(|c: char| + !(c.is_ascii_alphanumeric() || c == '_')) + .is_some() + { + false + } else { + true + } } } + // TODO: Complete verifications. + if args.area.is_none() && args.invert { anyhow::bail!("Cannot invert without a specified area."); } if let Some(a) = args.area { - for pos in vec![a.min, a.max] { + for pos in &[a.min, a.max] { anyhow::ensure!(pos.is_valid_node_pos(), "Area corner is outside map bounds: {}.", pos); } } - if let Some(sn) = &args.node { - verify_item_name(sn.as_str()) - .with_context(|| format!("Invalid node name: {}.", sn))?; + + macro_rules! verify_name { + ($name:expr, $msg:literal) => { + if let Some(n) = &$name { + anyhow::ensure!(is_valid_name(n), $msg, n); + } + } } - if let Some(rn) = &args.new_node { - verify_item_name(rn.as_str()) - .with_context(|| format!("Invalid replacement name: {}.", rn))?; + + verify_name!(args.node, "Invalid node name: {}"); + for n in &args.nodes { + anyhow::ensure!(is_valid_name(n), "Invalid node name: {}", n); } + verify_name!(args.new_node, "Invalid node name: {}"); + verify_name!(args.object, "Invalid object name: {}"); + verify_name!(args.item, "Invalid item name: {}"); Ok(()) } @@ -266,11 +292,18 @@ fn compute_thread(args: InstArgs, status: StatusServer) Some(conn) => Some(MapDatabase::new(conn)?), None => None }; - + // TODO: Standard warning? let func = commands[args.command.as_str()].func; let mut inst = InstBundle {args, status, db, idb}; func(&mut inst); + let fails = inst.status.get_status().blocks_failed; + if fails > 0 { + // TODO: log_warning + inst.status.log_info(format!( + "Skipped {} invalid/unsupported mapblocks.", fails)); + } + if inst.db.is_in_transaction() { inst.status.log_info("Committing..."); inst.db.commit_if_needed()?;