diff --git a/Manual.md b/Manual.md index 931a0d0..f74ae99 100644 --- a/Manual.md +++ b/Manual.md @@ -8,28 +8,28 @@ of Minetest. MapEditr reads and edits *map databases*, usually a file named `map.sqlite` within each Minetest world directory. As such, the terms "world" and "map" may -be used interchangeably. +be used interchangeably. Note that only SQLite format maps are currently +supported. -For most commands to work, the areas of the map to be read/modified must +For most commands to work, the parts of the map to be read/modified must already be generated. This can be done by either exploring the area in-game, or by using Minetest's built-in `/emergeblocks` command. MapEditr supports all maps created since Minetest version 0.4.2-rc1, released -July 2012. Any unsupported areas of the map will be skipped. Note that only -SQLite format maps are currently supported. +July 2012. Any unsupported parts of the map will be skipped. ## General usage -`mapeditr [-h] [-y] ` +`mapeditr [-h] [-y] ` Arguments: - `-h, --help`: Print help information and exit. - `-y, --yes`: Skip the default confirmation prompt (for those who feel brave). - ``: Path to the Minetest world/map to edit; this can be either a world -directory or a `map.sqlite` file within a world folder. This file will be -modified, so *always* shut down the game/server before executing any command. -- ``: Command to execute. See "Commands" section below. +directory or a `map.sqlite` file. This world/map will be modified, so *always* +shut down the game or server before executing any command. +- ``: Command to execute. See the "Commands" section below. ### Common command arguments @@ -37,11 +37,13 @@ modified, so *always* shut down the game/server before executing any command. area with corners at `p1` and `p2`, similarly to how WorldEdit's area selection works. Any two opposite corners can be used. These coordinates can be found using Minetest's F5 debug menu. -- Node/item names: includes `node`, `new_node`, etc. Must be the full name, +- Node/item names, including `node`, `new_node`, etc.: Must be the full name, e.g. "default:stone", not just "stone". ### Other tips +Optional arguments are indicated using [square brackets]. + Text-like arguments can be surrounded with "quotes" if they contain spaces. Due to technical limitations, MapEditr will often leave lighting glitches. To @@ -50,7 +52,7 @@ WorldEdit `//fixlight` command. ## Commands -TODO: Unify documentation style, provide examples. +TODO: Unify documentation style, argument ordering, provide examples. ### clone @@ -61,30 +63,41 @@ Clone (copy) the contents of an area to a new location. Arguments: - `--p1, --p2`: Area to clone. -- `--offset x y z`: Vector to shift the area by. For example, to copy an area -50 nodes downward (negative Y direction), use `--offset 0 -50 0`. Directions -may be determined using Minetest's F5 debug menu. +- `--offset x y z`: Vector to shift the area's contents by. For example, to +copy an area 50 nodes downward (negative Y direction), use `--offset 0 -50 0`. +Directions may be determined using Minetest's F5 debug menu. This command copies nodes, param1, param2, and metadata. Nothing will be copied from or into mapblocks that are not yet generated. +Examples: + +- Clone an area surrounding spawn 500 nodes west and 360 nodes north: +`clone --p1 200 80 200 --p2 -200 -15 -200 --offset -500 0 360` + ### deleteblocks Usage: `deleteblocks --p1 x y z --p2 x y z [--invert]` -Delete all mapblocks inside or outside an area. +Delete all mapblocks inside or outside an area. This command is often much +faster than Minetest's built-in `/deleteblocks` command. Arguments: - `--p1, --p2`: Area containing mapblocks to delete. By default, only mapblocks fully within this area will be deleted. -- `--invert`: If present, delete all mapblocks fully *outside* the given area. -Use with caution; you could erase a large portion of your world! +- `--invert`: Delete all mapblocks fully *outside* the given area. Use with +caution; you could erase a large portion of your world! **Note:** Deleting mapblocks is *not* the same as filling them with air! Mapgen will be invoked where the blocks were deleted, and this sometimes causes terrain glitches. +Examples: + +- Delete all saved mapblocks below y = -200 and above y = 200: +`deleteblocks --p1 -31000 -200 -31000 --p2 31000 200 31000 --invert` + ### deletemeta Usage: `deletemeta [--node ] [--p1 x y z] [--p2 x y z] [--invert]` @@ -98,7 +111,7 @@ Arguments: specified, metadata will be deleted from any node. - `--p1, --p2`: (Optional) Area in which to delete metadata. If not specified, metadata will be deleted everywhere. -- `--invert`: If present, delete metadata *outside* the given area. +- `--invert`: Delete metadata *outside* the given area. ### deleteobjects @@ -109,15 +122,22 @@ Delete certain objects (entities) and/or item entities (dropped items). Arguments: - `--obj `: (Optional) Name of object to delete, e.g. "boats:boat". -- `--items [items]`: If present, delete only item entities (dropped items). If +- `--items [items]`: (Optional) Delete only item entities (dropped items). If one or more item names are listed after `--items`, only those items will be deleted. - `--p1, --p2`: (Optional) Area in which to delete objects. If not specified, objects will be deleted everywhere. -- `--invert`: If present, delete objects *outside* the given area. +- `--invert`: Delete objects *outside* the given area. `--obj` and `--items` cannot be used simultaneously. +Examples: + +- Delete all objects: `deleteobjects` +- Delete all cart entities: `deleteobjects --obj carts:cart` +- Delete dropped stone and gravel: +`deleteobjects --items default:stone default:gravel` + ### deletetimers Usage: `deletetimers [--node ] [--p1 x y z] [--p2 x y z] [--invert]` @@ -126,17 +146,18 @@ Delete node timers of certain nodes. Arguments: -- `--node `: If specified, only delete node timers from nodes with the -given name. -- `--p1, --p2`: (Optional) Area in which to delete node timers. +- `--node `: (Optional) Name of node to delete node timers from. If not +specified, node timers of any node will be deleted. +- `--p1, --p2`: (Optional) Area in which to delete node timers. If not +specified, node timers will be deleted everywhere. - `--invert`: Delete node timers *outside* the given area. ### fill Usage: `fill --p1 x y z --p2 x y z [--invert] ` -Fills everything inside or outside an area with one node. Mapblocks that are -not yet generated will not be affected. +Set all nodes inside or outside an area. Mapblocks that are not yet generated +will not be affected. This command does not affect param2, node metadata, etc. @@ -144,7 +165,14 @@ Arguments: - `--p1, --p2`: Area to fill. - `--invert`: Fill all generated nodes *outside* the given area. -- ``: Name of node to fill the area with. +- ``: Name of the node to fill with. + +Examples: + +- Carve out a large hole in the ground: +`fill --p1 224 50 347 --p2 817 -40 73 air` +- Build a long obsidian glass wall travelling north/south: +`fill --p1 0 -30 -10000 --p2 0 30 10000 default:obsidian_glass` ### overlay @@ -154,52 +182,63 @@ Copy part or all of a source map into the main map. Arguments: -- ``: Path to the source map/world. This world will not be modified. -- `--p1, --p2`: Area to copy from. If not specified, MapEditr will try to -copy everything from the source map. -- `--invert`: If present, copy everything *outside* the given area. -- `--offset x y z`: Vector to shift nodes by when copying; default is no -offset. Currently, an offset cannot be used with an inverted selection. +- ``: Path to the source map/world. This map will not be modified. +- `--p1, --p2`: (Optional) Area to copy from. If not specified, MapEditr will +try to copy everything from the source map. +- `--invert`: Copy everything *outside* the given area. +- `--offset x y z`: (Optional) Vector to shift nodes by when copying; default +is no offset. Currently, an offset cannot be used with an inverted selection. + +**If an area and/or offset is used:** To ensure that all data is copied, make +sure at least the "edges" of the destination area are generated, or the entire +destination area if using an offset. This command will always copy nodes, param1, param2, and metadata. If no offset is used, objects/entities and node timers may also be copied. -To ensure that all data is copied, make sure the edges of the selection are -generated in the destination map, or the entire selection if an offset is used. +**Tip:** Overlay will be significantly faster if no offset is used. -**Tip:** Overlay will be significantly faster if no offset is used, as -mapblocks can be copied verbatim. +Examples (your world/map paths will vary): + +- Copy all of map `backup.sqlite` into the main world: +`overlay backup-maps/backup.sqlite` +- Copy world `test` into the main world, excluding the area within 120 nodes of +spawn: `overlay test --p1 -120 -120 -120 --p2 120 120 120 --invert` +- Copy an area from `map.sqlite` into the main world, moving it 32 nodes north: +`overlay map.sqlite --p1 6 36 -49 --p2 -9 74 -78 --offset 0 0 32` ### replaceininv -Usage: `replaceininv [--delete] [--deletemeta] [--nodes ] [--p1 x y z] [--p2 x y z] [--invert] [new_item]` +Usage: `replaceininv [new_item] [--delete] [--deletemeta] [--nodes ] [--p1 x y z] [--p2 x y z] [--invert]` Replace, delete, or modify items in certain node inventories. Arguments: -- ``: Name of the item to replace/delete -- `[new_item]`: Name of the new item, if replacing items. +- ``: Name of the item to replace/delete. +- `[new_item]`: (Optional) Name of the new item, if replacing items. - `--delete`: Delete items instead of replacing them. - `--deletemeta`: Delete metadata of affected items. May be used with or without `new_item`, depending on whether items should also be replaced. -- `--nodes `: Names of one or more nodes to modify inventories of. If -not specified, items will be modified in any node with an inventory. -- `--p1, --p2`: Area in which to modify node inventories. If not specified, -items will be modified everywhere. +- `--nodes `: (Optional) Names of one or more nodes to modify +inventories of. If not specified, items will be modified in any node with an +inventory. +- `--p1, --p2`: (Optional) Area in which to modify node inventories. If not +specified, items will be modified everywhere. - `--invert`: Modify node inventories *outside* the given area. Examples: -Replace all written books in chests with unwritten books, deleting metadata: - +- Delete all lava buckets: +`replaceininv bucket:bucket_lava --delete` +- Replace all written books in chests with unwritten books, deleting metadata: `replaceininv default:book_written default:book --deletemeta --nodes default:chest default:chest_locked` ### replacenodes -Usage: `replacenodes [--p1 x y z] [--p2 x y z] [--invert] ` +Usage: `replacenodes [--p1 x y z] [--p2 x y z] [--invert]` -Replace all of one node with another node. Can be used to remove unknown nodes +Replace one node with another node. Can also be used to remove unknown nodes or swap a node that changed names. This command does not affect param2, metadata, etc. @@ -208,28 +247,43 @@ Arguments: - ``: Name of node to replace. - ``: Name of node to replace with. -- `--p1, --p2`: Area in which to replace nodes. If not specified, nodes will be -replaced across the entire map. +- `--p1, --p2`: (Optional) Area in which to replace nodes. If not specified, +nodes will be replaced across the entire map. - `--invert`: Replace nodes *outside* the given area. +Examples: + +- Replace all legacy PB&J pup nodes with mese blocks: +`replacenodes pbj_pup:pbj_pup default:mese` +- Remove fire nodes near ground level: +`replacenodes fire:basic_flame air --p1 -31000 -80 -31000 --p2 31000 200 31000` + ### setmetavar -Usage: `setmetavar [--node ] [--p1 x y z] [--p2 x y z] [--invert] ` +Usage: `setmetavar [value] [--delete] [--nodes ] [--p1 x y z] [--p2 x y z] [--invert]` -Set or delete a variable in node metadata of certain nodes. This only works on metadata -where the variable is already set. +Set or delete a variable in node metadata of certain nodes. This only works on +nodes where the variable is already set. Arguments: - ``: Name of variable to set/delete, e.g. `infotext`, `formspec`, etc. -- ``: Value to set variable to, if setting a value. This should be a +- `[value]`: Value to set variable to, if setting a value. This should be a string. - `--delete`: Delete the variable. -- `--nodes `: Names of one or more nodes to modify. If not specified, -any node with the given variable will be modified. -- `--p1, --p2`: Area in which to modify node metadata. +- `--nodes `: (Optional) Names of one or more nodes to modify. If not +specified, any node with the given variable will be modified. +- `--p1, --p2`: (Optional) Area in which to modify node metadata. If not +specified, nodes will be modified everywhere. - `--invert`: Modify node metadata *outside* the given area. +Examples: + +- Clear infotext of signs: +`setmetavar infotext --delete --nodes default:sign_wall_steel default:sign_wall_wood` +- Set "player1" as the owner of all steel trapdoors: +`setmetavar owner player1 --nodes doors:trapdoor_steel` + ### setparam2 Usage: `setparam2 [--node ] [--p1 x y z] [--p2 x y z] [--invert] ` @@ -238,25 +292,26 @@ Set param2 values of certain nodes. Arguments: -- ``: New param2 value, between 0 and 255. -- `--node `: Name of node to modify. If not specified, the param2 values -of any node will be set. -- `--p1, --p2`: Area in which to set param2 values. +- `--node `: (Optional) Name of node to modify. If not specified, the +param2 values of any node will be set. +- `--p1, --p2`: (Optional) Area in which to set param2 values. If not +specified, param2 will be set everywhere. - `--invert`: Set param2 values *outside* the given area. +- ``: New param2 value, between 0 and 255. -An area and/or node is required for setparam2. +An area and/or node is required for this command. ### vacuum Usage: `vacuum` -Vacuums the database. This reduces the size of the database, but may take a -long time. +Rebuild the map database to reduce its size. Vacuuming may take a long time for +large maps. -All this does is perform an SQLite `VACUUM` command. This shrinks and optimizes -the database by efficiently "repacking" all mapblocks. No map data is changed -or deleted. +This command simply executes the SQLite `VACUUM` command, which shrinks and +optimizes the map database by efficiently "repacking" all mapblocks. No map +data is changed or deleted. -**Note:** Because data is copied into another file, vacuum could require -as much free disk space as is already occupied by the map. For example, if -map.sqlite is 10 GB, make sure you have **at least 10 GB** of free space! +**Note:** Because data is temporarily copied into another file, vacuum could +require as much free disk space as is already occupied by the map. For example, +if map.sqlite is 10 GB, make sure you have **at least 10 GB** of free space! diff --git a/README.md b/README.md index 52205c9..eba4a59 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,11 @@ runs much faster than the default, unoptimized version. For an overview of how MapEditr works and a listing of commands and their usages, see [Manual.md](Manual.md). -Some useful things you can do with MapEditr: +These are just a few of the useful things you can do with MapEditr: - Remove unknown nodes left by old mods with `replacenodes`. - Build extremely long walls and roads in seconds using `fill`. +- Selectively delete entities and/or dropped items using `deleteobjects`. - Combine multiple worlds or map saves with `overlay`. ## License diff --git a/src/cmd_line.rs b/src/cmd_line.rs index af1ec76..f024712 100644 --- a/src/cmd_line.rs +++ b/src/cmd_line.rs @@ -316,7 +316,7 @@ pub fn run_cmd_line() { }, ServerEvent::ConfirmRequest => { newline_if(&mut need_newline); - status.confirm(get_confirmation()); + status.send_confirmation(get_confirmation()); }, }, Err(err) => { diff --git a/src/commands/clone.rs b/src/commands/clone.rs index da80a67..7f54f9f 100644 --- a/src/commands/clone.rs +++ b/src/commands/clone.rs @@ -128,7 +128,7 @@ pub fn get_command() -> Command { verify_args: Some(verify_args), args: vec![ (ArgType::Area(true), "Area to clone"), - (ArgType::Offset(true), "Vector to shift the area by") + (ArgType::Offset(true), "Vector to shift the area's contents by") ], help: "Clone (copy) the contents of an area to a new location." } diff --git a/src/commands/delete_blocks.rs b/src/commands/delete_blocks.rs index 68e33b6..ec91e85 100644 --- a/src/commands/delete_blocks.rs +++ b/src/commands/delete_blocks.rs @@ -25,8 +25,7 @@ pub fn get_command() -> Command { args: vec![ (ArgType::Area(true), "Area containing mapblocks to delete"), (ArgType::Invert, - "If present, delete all mapblocks fully *outside* the given \ - area.") + "Delete all mapblocks fully *outside* the given area.") ], help: "Delete all mapblocks inside or outside an area." } diff --git a/src/commands/delete_meta.rs b/src/commands/delete_meta.rs index fd189b4..3153c2f 100644 --- a/src/commands/delete_meta.rs +++ b/src/commands/delete_meta.rs @@ -83,10 +83,9 @@ pub fn get_command() -> Command { func: delete_metadata, verify_args: Some(verify_args), args: vec![ + (ArgType::Node(false), "Name of node to delete metadata from"), (ArgType::Area(false), "Area in which to delete metadata"), - (ArgType::Invert, - "If present, delete metadata *outside* the given area."), - (ArgType::Node(false), "Name of node to delete metadata from") + (ArgType::Invert, "Delete metadata *outside* the given area."), ], help: "Delete node metadata of certain nodes." } diff --git a/src/commands/delete_objects.rs b/src/commands/delete_objects.rs index 884d528..0197931 100644 --- a/src/commands/delete_objects.rs +++ b/src/commands/delete_objects.rs @@ -132,13 +132,12 @@ pub fn get_command() -> Command { func: delete_objects, verify_args: Some(verify_args), args: vec![ - (ArgType::Area(false), "Area in which to delete objects"), - (ArgType::Invert, - "If present, delete objects *outside* the given area."), (ArgType::Object, "Name of object to delete"), (ArgType::Items, - "If present, delete item entities. Optionally list one or \ - more item names after `--items` to delete only those items."), + "Delete only item entities. Optionally list one or more item \ + names after `--items` to delete only those items."), + (ArgType::Area(false), "Area in which to delete objects"), + (ArgType::Invert, "Delete objects *outside* the given area."), ], help: "Delete certain objects and/or item entities." } diff --git a/src/commands/delete_timers.rs b/src/commands/delete_timers.rs index 8132356..d6c9225 100644 --- a/src/commands/delete_timers.rs +++ b/src/commands/delete_timers.rs @@ -68,11 +68,9 @@ pub fn get_command() -> Command { func: delete_timers, verify_args: None, args: vec![ - (ArgType::Area(false), "Area in which to delete timers"), + (ArgType::Node(false), "Name of node to delete node timers from"), + (ArgType::Area(false), "Area in which to delete node timers"), (ArgType::Invert, "Delete node timers *outside* the given area."), - (ArgType::Node(false), - "Node to delete timers from. If not specified, node timers \ - will be deleted from any node.") ], help: "Delete node timers of certain nodes." } diff --git a/src/commands/fill.rs b/src/commands/fill.rs index 7f05359..3380409 100644 --- a/src/commands/fill.rs +++ b/src/commands/fill.rs @@ -82,8 +82,8 @@ pub fn get_command() -> Command { (ArgType::Area(true), "Area to fill"), (ArgType::Invert, "Fill all generated nodes *outside* the given area."), - (ArgType::NewNode, "Name of node to fill the area with"), + (ArgType::NewNode, "Name of the node to fill with"), ], - help: "Fill the entire area with one node." + help: "Set all nodes inside or outside an area." } } diff --git a/src/commands/overlay.rs b/src/commands/overlay.rs index dda1754..7134a52 100644 --- a/src/commands/overlay.rs +++ b/src/commands/overlay.rs @@ -228,8 +228,7 @@ pub fn get_command() -> Command { (ArgType::InputMapPath, "Path to the source map/world"), (ArgType::Area(false), "Area to copy from. If not specified, \ everything from the source map will be copied."), - (ArgType::Invert, - "If present, copy everything *outside* the given area."), + (ArgType::Invert, "Copy everything *outside* the given area."), (ArgType::Offset(false), "Vector to shift nodes by when copying"), ], help: "Copy part or all of a source map into the main map." diff --git a/src/commands/replace_nodes.rs b/src/commands/replace_nodes.rs index e157348..c96c466 100644 --- a/src/commands/replace_nodes.rs +++ b/src/commands/replace_nodes.rs @@ -146,6 +146,6 @@ pub fn get_command() -> Command { (ArgType::Area(false), "Area in which to replace nodes"), (ArgType::Invert, "Replace nodes *outside* the given area.") ], - help: "Replace all of one node with another node." + help: "Replace one node with another node." } } diff --git a/src/commands/set_param2.rs b/src/commands/set_param2.rs index e9d6a74..d181e83 100644 --- a/src/commands/set_param2.rs +++ b/src/commands/set_param2.rs @@ -60,8 +60,6 @@ fn set_param2(inst: &mut InstBundle) { inst.status.begin_editing(); let mut count: u64 = 0; - use crate::time_keeper::TimeKeeper; - let mut tk = TimeKeeper::new(); for key in keys { inst.status.inc_done(); @@ -81,7 +79,6 @@ fn set_param2(inst: &mut InstBundle) { .filter(|a| a.contains_block(pos) != a.touches_block(pos)) { // Modify part of block let block_part = area.rel_block_overlap(pos).unwrap(); - let _t = tk.get_timer("set_param2_partial"); count += set_param2_partial(&mut block, block_part, inst.args.invert, node_id, param2_val); } else { // Modify whole block @@ -102,7 +99,6 @@ fn set_param2(inst: &mut InstBundle) { } inst.status.end_editing(); - tk.print(&mut inst.status); inst.status.log_info(format!("Set param2 of {} nodes.", fmt_big_num(count))); } @@ -122,10 +118,10 @@ pub fn get_command() -> Command { func: set_param2, verify_args: Some(verify_args), args: vec![ - (ArgType::Param2, "New param2 value, between 0 and 255"), - (ArgType::Node(false), "Node to set param2 values of"), + (ArgType::Node(false), "Name of node to modify"), (ArgType::Area(false), "Area in which to set param2 values"), (ArgType::Invert, "Set param2 values *outside* the given area."), + (ArgType::Param2, "New param2 value, between 0 and 255"), ], help: "Set param2 values of certain nodes." } diff --git a/src/commands/vacuum.rs b/src/commands/vacuum.rs index dd45ffd..8e6caad 100644 --- a/src/commands/vacuum.rs +++ b/src/commands/vacuum.rs @@ -13,7 +13,7 @@ fn vacuum(inst: &mut InstBundle) { match res { Ok(_) => { - inst.status.log_info(format!("Completed vacuum.")); + inst.status.log_info("Completed vacuum."); }, Err(e) => inst.status.log_error(format!("Vacuum failed: {}.", e)) } diff --git a/src/instance.rs b/src/instance.rs index 46c8aad..9d3599f 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -8,6 +8,7 @@ use crate::spatial::{Vec3, Area, MAP_LIMIT}; use crate::map_database::MapDatabase; use crate::commands; use crate::commands::ArgResult; +use crate::utils::fmt_big_num; #[derive(Clone)] @@ -200,7 +201,7 @@ impl StatusClient { &self.event_rx } - pub fn confirm(&self, choice: bool) { + pub fn send_confirmation(&self, choice: bool) { self.event_tx.send(ClientEvent::ConfirmResponse(choice)).unwrap(); } } @@ -235,8 +236,6 @@ pub struct InstBundle<'a> { fn verify_args(args: &InstArgs) -> anyhow::Result<()> { - // TODO: Complete verifications. - if args.area.is_none() && args.invert { anyhow::bail!("Cannot invert without a specified area."); } @@ -314,7 +313,7 @@ fn open_map(path: PathBuf, flags: sqlite::OpenFlags) if with_file.is_file() { with_file } else { - anyhow::bail!("could not find map file"); + anyhow::bail!("Could not find the map file."); } }; @@ -335,17 +334,20 @@ fn compute_thread(args: InstArgs, status: StatusServer) -> anyhow::Result<()> { } } - let db_conn = open_map(PathBuf::from(&args.map_path), - sqlite::OpenFlags::new().set_read_write())?; + let db_conn = open_map( + PathBuf::from(&args.map_path), + sqlite::OpenFlags::new().set_read_write() + ).context("Failed to open main world/map.")?; let db = MapDatabase::new(&db_conn) - .context("Failed to open main world/map.")?; + .context("Main world or map database is invalid.")?; - let idb_conn = args.input_map_path.as_deref().map( - |imp| open_map(PathBuf::from(imp), - sqlite::OpenFlags::new().set_read_only()) - ).transpose().context("Failed to open input world/map.")?; + let idb_conn = args.input_map_path.as_deref() + .map(|imp| open_map(PathBuf::from(imp), + sqlite::OpenFlags::new().set_read_only())) + .transpose().context("Failed to open input world/map.")?; let idb = match &idb_conn { - Some(conn) => Some(MapDatabase::new(conn)?), + Some(conn) => Some(MapDatabase::new(conn) + .context("Input world or map database is invalid.")?), None => None }; @@ -370,7 +372,9 @@ fn compute_thread(args: InstArgs, status: StatusServer) -> anyhow::Result<()> { let fails = inst.status.get_status().blocks_failed; if fails > 0 { inst.status.log_info(format!( - "Skipped {} invalid/unsupported mapblocks.", fails)); + "Skipped {} invalid/unsupported mapblocks.", + fmt_big_num(fails as u64) + )); } if inst.db.is_in_transaction() { diff --git a/src/main.rs b/src/main.rs index 040c84c..1033151 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ -mod time_keeper; +// Kept for testing purposes +// mod time_keeper; mod spatial; mod utils; mod map_database; @@ -9,8 +10,7 @@ mod commands; mod cmd_line; -// TODO: Check for unnecessary #derives! fn main() { - // TODO: Add GUI. hmm... + // TODO: Add a GUI. hmm... cmd_line::run_cmd_line(); } diff --git a/src/map_block/compression.rs b/src/map_block/compression.rs index ad9d8dd..f815ee2 100644 --- a/src/map_block/compression.rs +++ b/src/map_block/compression.rs @@ -34,6 +34,10 @@ impl Compress for Vec { } +/// Stores some data and its zlib-compressed equivalent. +/// +/// The goal of this struct is to avoid recompressing data that was not +/// modified from its original compressed form. #[derive(Clone, Debug)] pub struct ZlibContainer { compressed: Option>, diff --git a/src/map_block/map_block.rs b/src/map_block/map_block.rs index 6ed44cf..a970c5a 100644 --- a/src/map_block/map_block.rs +++ b/src/map_block/map_block.rs @@ -12,7 +12,7 @@ pub fn is_valid_generated(data: &[u8]) -> bool { } -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct MapBlock { pub version: u8, pub flags: u8, diff --git a/src/map_block/metadata.rs b/src/map_block/metadata.rs index 5f4bd1a..de5aca2 100644 --- a/src/map_block/metadata.rs +++ b/src/map_block/metadata.rs @@ -9,7 +9,7 @@ use memmem::{Searcher, TwoWaySearcher}; const END_STR: &[u8; 13] = b"EndInventory\n"; -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct NodeMetadata { pub vars: HashMap, (Vec, bool)>, pub inv: Vec @@ -100,20 +100,21 @@ impl NodeMetadataListExt for NodeMetadataList { fn serialize(&self, block_version: u8) -> Vec { let buf = Vec::new(); let mut data = Cursor::new(buf); + // Skip empty metadata when serializing. + let count = self.iter().filter(|&(_, m)| !m.is_empty()).count(); - if self.len() == 0 { + if count == 0 { data.write_u8(0).unwrap(); } else { let version = if block_version >= 28 { 2 } else { 1 }; data.write_u8(version).unwrap(); - data.write_u16::(self.len() as u16).unwrap(); + data.write_u16::(count as u16).unwrap(); for (&pos, meta) in self { - if meta.is_empty() { - continue; // Skip empty metadata. + if !meta.is_empty() { + data.write_u16::(pos).unwrap(); + meta.serialize(&mut data, version); } - data.write_u16::(pos).unwrap(); - meta.serialize(&mut data, version); } } diff --git a/src/map_block/name_id_map.rs b/src/map_block/name_id_map.rs index fdc526c..fa2c8a1 100644 --- a/src/map_block/name_id_map.rs +++ b/src/map_block/name_id_map.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; /// Maps 16-bit node IDs to actual node names. /// /// Relevant Minetest source file: /src/nameidmapping.cpp -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct NameIdMap(pub BTreeMap>); impl NameIdMap { diff --git a/src/map_block/node_timer.rs b/src/map_block/node_timer.rs index e1b732d..7dfa1f3 100644 --- a/src/map_block/node_timer.rs +++ b/src/map_block/node_timer.rs @@ -2,7 +2,7 @@ use super::*; use std::cmp::min; -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct NodeTimer { pub pos: u16, pub timeout: u32,