Completion Part 5

Why is writing documentation so hard?
master
random-geek 2021-04-15 00:44:59 -07:00
parent 661889b852
commit 6ff1da5595
20 changed files with 178 additions and 123 deletions

191
Manual.md
View File

@ -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] <map> <subcommand>`
`mapeditr [-h] [-y] <map> <SUBCOMMAND>`
Arguments:
- `-h, --help`: Print help information and exit.
- `-y, --yes`: Skip the default confirmation prompt (for those who feel brave).
- `<map>`: 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.
- `<subcommand>`: 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.
- `<SUBCOMMAND>`: 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 <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 <object>`: (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 <node>] [--p1 x y z] [--p2 x y z] [--invert]`
@ -126,17 +146,18 @@ Delete node timers of certain nodes.
Arguments:
- `--node <node>`: If specified, only delete node timers from nodes with the
given name.
- `--p1, --p2`: (Optional) Area in which to delete node timers.
- `--node <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] <new_node>`
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.
- `<new_node>`: Name of node to fill the area with.
- `<new_node>`: 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:
- `<input_map>`: 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.
- `<input_map>`: 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 <nodes>] [--p1 x y z] [--p2 x y z] [--invert] <item> [new_item]`
Usage: `replaceininv <item> [new_item] [--delete] [--deletemeta] [--nodes <nodes>] [--p1 x y z] [--p2 x y z] [--invert]`
Replace, delete, or modify items in certain node inventories.
Arguments:
- `<item>`: Name of the item to replace/delete
- `[new_item]`: Name of the new item, if replacing items.
- `<item>`: 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 <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 <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] <node> <new_node>`
Usage: `replacenodes <node> <new_node> [--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:
- `<node>`: Name of node to replace.
- `<new_node>`: 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 <node>] [--p1 x y z] [--p2 x y z] [--invert] <key> <value>`
Usage: `setmetavar <key> [value] [--delete] [--nodes <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:
- `<key>`: Name of variable to set/delete, e.g. `infotext`, `formspec`, etc.
- `<value>`: 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 <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 <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 <node>] [--p1 x y z] [--p2 x y z] [--invert] <param2>`
@ -238,25 +292,26 @@ Set param2 values of certain nodes.
Arguments:
- `<param2>`: New param2 value, between 0 and 255.
- `--node <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 <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.
- `<param2>`: 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!

View File

@ -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

View File

@ -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) => {

View File

@ -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."
}

View File

@ -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."
}

View File

@ -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."
}

View File

@ -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."
}

View File

@ -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."
}

View File

@ -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."
}
}

View File

@ -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."

View File

@ -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."
}
}

View File

@ -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."
}

View File

@ -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))
}

View File

@ -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() {

View File

@ -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();
}

View File

@ -34,6 +34,10 @@ impl Compress for Vec<u8> {
}
/// 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<T: Compress> {
compressed: Option<Vec<u8>>,

View File

@ -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,

View File

@ -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<u8>, (Vec<u8>, bool)>,
pub inv: Vec<u8>
@ -100,20 +100,21 @@ impl NodeMetadataListExt for NodeMetadataList {
fn serialize(&self, block_version: u8) -> Vec<u8> {
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::<BigEndian>(self.len() as u16).unwrap();
data.write_u16::<BigEndian>(count as u16).unwrap();
for (&pos, meta) in self {
if meta.is_empty() {
continue; // Skip empty metadata.
if !meta.is_empty() {
data.write_u16::<BigEndian>(pos).unwrap();
meta.serialize(&mut data, version);
}
data.write_u16::<BigEndian>(pos).unwrap();
meta.serialize(&mut data, version);
}
}

View File

@ -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<u16, Vec<u8>>);
impl NameIdMap {

View File

@ -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,