Completion Part 4
parent
b18b0e96cc
commit
661889b852
65
Manual.md
65
Manual.md
|
@ -174,27 +174,26 @@ mapblocks can be copied verbatim.
|
|||
|
||||
Usage: `replaceininv [--delete] [--deletemeta] [--nodes <nodes>] [--p1 x y z] [--p2 x y z] [--invert] <item> [new_item]`
|
||||
|
||||
Replace or delete certain items in node inventories.
|
||||
Replace, delete, or modify items in certain node inventories.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `item`: Name of item to replace/delete
|
||||
- `new_item`: Name of new item, if replacing items.
|
||||
- `<item>`: Name of the item to replace/delete
|
||||
- `[new_item]`: Name of the new item, if replacing items.
|
||||
- `--delete`: Delete items instead of replacing them.
|
||||
- `--deletemeta`: Delete metadata of 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 replace in. If not specified, the
|
||||
item will be replaced in all node inventories.
|
||||
- `--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 replaced in all node inventories.
|
||||
- `--invert`: Only modify node inventories *outside* the given area.
|
||||
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:
|
||||
|
||||
`replaceininv default:book_written default:book --deletemeta --nodes
|
||||
default:chest default:chest_locked`
|
||||
`replaceininv default:book_written default:book --deletemeta --nodes default:chest default:chest_locked`
|
||||
|
||||
### replacenodes
|
||||
|
||||
|
@ -207,41 +206,45 @@ This command does not affect param2, metadata, etc.
|
|||
|
||||
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.
|
||||
- `--invert`: Only replace nodes *outside* the given area.
|
||||
- `<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.
|
||||
- `--invert`: Replace nodes *outside* the given area.
|
||||
|
||||
### setmetavar
|
||||
|
||||
Usage: `setmetavar [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <key> <value>`
|
||||
|
||||
Set a variable in node metadata. 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 metadata
|
||||
where the variable is already set.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `key`: Name of variable to set, e.g. `infotext`, `formspec`, etc.
|
||||
- `value`: Value to set variable to. This should be a string.
|
||||
- `--node`: Name of node to modify. If not specified, the variable will be
|
||||
set for all nodes that have it.
|
||||
- `--p1, --p2`: Area in which to modify nodes.
|
||||
- `--invert`: Only modify nodes *outside* the given area.
|
||||
- `<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
|
||||
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.
|
||||
- `--invert`: Modify node metadata *outside* the given area.
|
||||
|
||||
### setparam2
|
||||
|
||||
Usage: `setparam2 [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <param2_val>`
|
||||
Usage: `setparam2 [--node <node>] [--p1 x y z] [--p2 x y z] [--invert] <param2>`
|
||||
|
||||
Set param2 values of a certain node and/or within a certain area.
|
||||
Set param2 values of certain nodes.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `param2_val`: Param2 value to set, between 0 and 255.
|
||||
- `--node`: Name of node to modify. If not specified, the param2 values of
|
||||
all nodes will be set.
|
||||
- `--p1, --p2`: Area in which to set param2.
|
||||
- `--invert`: Only set param2 *outside* the given area.
|
||||
- `<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.
|
||||
- `--invert`: Set param2 values *outside* the given area.
|
||||
|
||||
An area and/or node is required for setparam2.
|
||||
|
||||
### vacuum
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
|
|||
.help(help_msg)
|
||||
];
|
||||
}
|
||||
// TODO: Ensure arguments are correctly defined.
|
||||
|
||||
let arg = match arg_type {
|
||||
ArgType::Area(_) => unreachable!(),
|
||||
ArgType::InputMapPath =>
|
||||
|
@ -65,10 +65,9 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
|
|||
.value_names(&["x", "y", "z"])
|
||||
.required(req),
|
||||
ArgType::Node(req) => {
|
||||
let a = Arg::with_name("node")
|
||||
.required(req);
|
||||
let a = Arg::with_name("node");
|
||||
if req {
|
||||
a
|
||||
a.required(true)
|
||||
} else {
|
||||
a.long("node").takes_value(true)
|
||||
}
|
||||
|
@ -96,22 +95,21 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
|
|||
ArgType::NewItem =>
|
||||
Arg::with_name("new_item")
|
||||
.takes_value(true),
|
||||
ArgType::Delete =>
|
||||
Arg::with_name("delete")
|
||||
.long("delete"),
|
||||
ArgType::DeleteMeta =>
|
||||
Arg::with_name("delete_meta")
|
||||
.long("deletemeta"),
|
||||
ArgType::DeleteItem =>
|
||||
Arg::with_name("delete_item")
|
||||
.long("delete"),
|
||||
ArgType::Key =>
|
||||
Arg::with_name("key")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
ArgType::Value =>
|
||||
Arg::with_name("value")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
ArgType::Param2Val =>
|
||||
Arg::with_name("param2_val")
|
||||
.takes_value(true),
|
||||
ArgType::Param2 =>
|
||||
Arg::with_name("param2")
|
||||
.required(true),
|
||||
}.help(help_msg);
|
||||
|
||||
|
@ -129,6 +127,7 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
|||
SubCommand::with_name(cmd_name)
|
||||
.about(cmd.help)
|
||||
.args(&args)
|
||||
.after_help("For additional information, see the manual.")
|
||||
});
|
||||
|
||||
let app = App::new("MapEditr")
|
||||
|
@ -144,10 +143,9 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
|||
.global(true)
|
||||
.help("Skip the default confirmation prompt.")
|
||||
)
|
||||
// TODO: Move map arg to subcommands?
|
||||
.arg(Arg::with_name("map")
|
||||
.required(true)
|
||||
.help("Path to world directory or map database to edit.")
|
||||
.help("Path to world directory or map database to edit")
|
||||
)
|
||||
.setting(AppSettings::SubcommandRequired)
|
||||
.subcommands(app_commands);
|
||||
|
@ -164,9 +162,9 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
|||
input_map_path: sub_matches.value_of("input_map").map(str::to_string),
|
||||
area: {
|
||||
let p1_maybe = sub_matches.values_of("p1").map(arg_to_pos)
|
||||
.transpose().context("Invalid p1 value")?;
|
||||
.transpose().context("Invalid p1 value.")?;
|
||||
let p2_maybe = sub_matches.values_of("p2").map(arg_to_pos)
|
||||
.transpose().context("Invalid p2 value")?;
|
||||
.transpose().context("Invalid p2 value.")?;
|
||||
if let (Some(p1), Some(p2)) = (p1_maybe, p2_maybe) {
|
||||
Some(Area::from_unsorted(p1, p2))
|
||||
} else {
|
||||
|
@ -175,7 +173,7 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
|||
},
|
||||
invert: sub_matches.is_present("invert"),
|
||||
offset: sub_matches.values_of("offset").map(arg_to_pos).transpose()
|
||||
.context("Invalid offset value")?,
|
||||
.context("Invalid offset value.")?,
|
||||
node: sub_matches.value_of("node").map(str::to_string),
|
||||
nodes: sub_matches.values_of("nodes").iter_mut().flatten()
|
||||
.map(str::to_string).collect(),
|
||||
|
@ -185,11 +183,11 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
|||
items: sub_matches.values_of("items")
|
||||
.map(|v| v.map(str::to_string).collect()),
|
||||
new_item: sub_matches.value_of("new_item").map(str::to_string),
|
||||
delete: sub_matches.is_present("delete"),
|
||||
delete_meta: sub_matches.is_present("delete_meta"),
|
||||
delete_item: sub_matches.is_present("delete_item"),
|
||||
key: sub_matches.value_of("key").map(str::to_string),
|
||||
value: sub_matches.value_of("value").map(str::to_string),
|
||||
param2_val: sub_matches.value_of("param2_val").map(|val| val.parse())
|
||||
param2: sub_matches.value_of("param2_val").map(|val| val.parse())
|
||||
.transpose().context("Invalid param2 value.")?,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@ use crate::instance::{ArgType, InstArgs, InstBundle};
|
|||
use crate::map_block::{MapBlock, NodeMetadataList, NodeMetadataListExt};
|
||||
use crate::utils::{query_keys, to_bytes, fmt_big_num};
|
||||
|
||||
const NEWLINE: u8 = b'\n';
|
||||
const SPACE: u8 = b' ';
|
||||
|
||||
|
||||
fn do_replace(inv: &mut Vec<u8>, item: &[u8], new_item: &[u8], del_meta: bool)
|
||||
-> u64
|
||||
{
|
||||
const NEWLINE: u8 = b'\n';
|
||||
const SPACE: u8 = b' ';
|
||||
|
||||
let delete = new_item.is_empty();
|
||||
let mut new_inv = Vec::with_capacity(inv.len());
|
||||
let mut new_inv = Vec::new();
|
||||
let mut mods = 0;
|
||||
|
||||
for line in inv.split(|&x| x == NEWLINE) {
|
||||
|
@ -66,7 +66,7 @@ fn do_replace(inv: &mut Vec<u8>, item: &[u8], new_item: &[u8], del_meta: bool)
|
|||
fn replace_in_inv(inst: &mut InstBundle) {
|
||||
let item = to_bytes(inst.args.item.as_ref().unwrap());
|
||||
let new_item = inst.args.new_item.as_ref().map(to_bytes)
|
||||
.unwrap_or(if inst.args.delete_item { vec![] } else { item.clone() });
|
||||
.unwrap_or(if inst.args.delete { vec![] } else { item.clone() });
|
||||
|
||||
let nodes: Vec<_> = inst.args.nodes.iter().map(to_bytes).collect();
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
|
@ -132,12 +132,14 @@ fn replace_in_inv(inst: &mut InstBundle) {
|
|||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.new_item.is_none() && !args.delete_item && !args.delete_meta {
|
||||
if args.new_item.is_none() && !args.delete && !args.delete_meta {
|
||||
return ArgResult::error(
|
||||
"new_item is required unless --delete or --deletemeta is used.");
|
||||
} else if args.new_item.is_some() && args.delete_item {
|
||||
} else if args.new_item.is_some() && args.delete {
|
||||
return ArgResult::error(
|
||||
"Cannot delete items if new_item is specified.");
|
||||
} else if args.item == args.new_item && !args.delete_meta {
|
||||
return ArgResult::error("item and new_item cannot be the same.");
|
||||
}
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
@ -150,13 +152,15 @@ pub fn get_command() -> Command {
|
|||
args: vec![
|
||||
(ArgType::Item, "Name of the item to replace/delete"),
|
||||
(ArgType::NewItem, "Name of the new item, if replacing items."),
|
||||
(ArgType::Delete, "Delete items instead of replacing them."),
|
||||
(ArgType::DeleteMeta, "Delete metadata of affected items."),
|
||||
(ArgType::DeleteItem, "Delete items instead of replacing them."),
|
||||
(ArgType::Area(false), "Area in which to modify inventories"),
|
||||
(ArgType::Invert, "Modify inventories outside the given area."),
|
||||
(ArgType::Nodes, "Names of nodes to modify inventories of"),
|
||||
(ArgType::Nodes,
|
||||
"Names of one or more nodes to modify inventories of"),
|
||||
(ArgType::Area(false), "Area in which to modify node inventories"),
|
||||
(ArgType::Invert,
|
||||
"Modify node inventories *outside* the given area."),
|
||||
],
|
||||
help: "Replace or delete items in node inventories."
|
||||
help: "Replace, delete, or modify items in certain node inventories."
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ fn do_replace(
|
|||
let block_pos = Vec3::from_block_key(key);
|
||||
let mut replaced = 0;
|
||||
|
||||
// Replace nodes in a portion of a mapblock.
|
||||
// Replace nodes in a portion of the mapblock.
|
||||
if area
|
||||
.filter(|a| a.contains_block(block_pos) != a.touches_block(block_pos))
|
||||
.is_some()
|
||||
|
@ -144,7 +144,7 @@ pub fn get_command() -> Command {
|
|||
(ArgType::Node(true), "Name of node to replace"),
|
||||
(ArgType::NewNode, "Name of node to replace with"),
|
||||
(ArgType::Area(false), "Area in which to replace nodes"),
|
||||
(ArgType::Invert, "Replace nodes outside the given area")
|
||||
(ArgType::Invert, "Replace nodes *outside* the given area.")
|
||||
],
|
||||
help: "Replace all of one node with another node."
|
||||
}
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
use super::Command;
|
||||
use super::{Command, ArgResult};
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::Vec3;
|
||||
use crate::instance::{ArgType, InstBundle};
|
||||
use crate::instance::{ArgType, InstArgs, InstBundle};
|
||||
use crate::map_block::{MapBlock, NodeMetadataList, NodeMetadataListExt};
|
||||
use crate::utils::{query_keys, to_bytes, fmt_big_num};
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.value.is_none() && !args.delete {
|
||||
return ArgResult::error(
|
||||
"value is required unless deleting the variable.");
|
||||
} else if args.value.is_some() && args.delete {
|
||||
return ArgResult::error(
|
||||
"value cannot be used when deleting the variable.");
|
||||
} else if args.value == Some(String::new()) {
|
||||
return ArgResult::error("Metadata value cannot be empty.");
|
||||
}
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
fn set_meta_var(inst: &mut InstBundle) {
|
||||
// TODO: Bytes input, create/delete variables
|
||||
// TODO: Bytes input
|
||||
let key = to_bytes(inst.args.key.as_ref().unwrap());
|
||||
let value = to_bytes(inst.args.value.as_ref().unwrap());
|
||||
let value = to_bytes(inst.args.value.as_ref().unwrap_or(&String::new()));
|
||||
let nodes: Vec<_> = inst.args.nodes.iter().map(to_bytes).collect();
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
|
@ -41,10 +55,9 @@ fn set_meta_var(inst: &mut InstBundle) {
|
|||
|
||||
for (&idx, data) in &mut meta {
|
||||
let pos = Vec3::from_u16_key(idx);
|
||||
let abs_pos = pos + block_corner;
|
||||
|
||||
if let Some(a) = inst.args.area {
|
||||
if a.contains(abs_pos) == inst.args.invert {
|
||||
if a.contains(pos + block_corner) == inst.args.invert {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +67,13 @@ fn set_meta_var(inst: &mut InstBundle) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(val) = data.vars.get_mut(&key) {
|
||||
val.0 = value.clone();
|
||||
if data.vars.contains_key(&key) {
|
||||
if inst.args.delete {
|
||||
// Note: serialize() will cull any newly empty metadata.
|
||||
data.vars.remove(&key);
|
||||
} else {
|
||||
data.vars.get_mut(&key).unwrap().0 = value.clone();
|
||||
}
|
||||
modified = true;
|
||||
count += 1;
|
||||
}
|
||||
|
@ -76,17 +94,18 @@ fn set_meta_var(inst: &mut InstBundle) {
|
|||
pub fn get_command() -> Command {
|
||||
Command {
|
||||
func: set_meta_var,
|
||||
verify_args: None,
|
||||
verify_args: Some(verify_args),
|
||||
args: vec![
|
||||
(ArgType::Key, "Name of key to set in metadata"),
|
||||
(ArgType::Value, "Value to set in metadata"),
|
||||
(ArgType::Area(false),
|
||||
"Optional area in which to modify node metadata"),
|
||||
(ArgType::Invert, "Modify node metadata outside the given area."),
|
||||
(ArgType::Key, "Name of variable to set/delete"),
|
||||
(ArgType::Value, "Value to set variable to, if setting a value"),
|
||||
(ArgType::Delete, "Delete the variable."),
|
||||
(ArgType::Nodes,
|
||||
"Names of one or more nodes to modify. If not specified, all \
|
||||
nodes with the specified variable will be modified.")
|
||||
"Names of one or more nodes to modify. If not specified, any \
|
||||
node with the given variable will be modified."),
|
||||
(ArgType::Area(false),
|
||||
"Area in which to modify node metadata"),
|
||||
(ArgType::Invert, "Modify node metadata *outside* the given area."),
|
||||
],
|
||||
help: "Set a variable in node metadata."
|
||||
help: "Set or delete a variable in node metadata of certain nodes."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ fn set_param2_partial(block: &mut MapBlock, area: Area, invert: bool,
|
|||
|
||||
|
||||
fn set_param2(inst: &mut InstBundle) {
|
||||
let param2_val = inst.args.param2_val.unwrap();
|
||||
let param2_val = inst.args.param2.unwrap();
|
||||
let node = inst.args.node.as_ref().map(to_bytes);
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
|
@ -103,13 +103,14 @@ fn set_param2(inst: &mut InstBundle) {
|
|||
|
||||
inst.status.end_editing();
|
||||
tk.print(&mut inst.status);
|
||||
inst.status.log_info(format!("{} nodes set.", fmt_big_num(count)));
|
||||
inst.status.log_info(format!("Set param2 of {} nodes.",
|
||||
fmt_big_num(count)));
|
||||
}
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.area.is_none() && args.node.is_none() {
|
||||
return ArgResult::error("An area and/or node must be provided.");
|
||||
return ArgResult::error("An area and/or node is required.");
|
||||
}
|
||||
|
||||
ArgResult::Ok
|
||||
|
@ -121,11 +122,11 @@ pub fn get_command() -> Command {
|
|||
func: set_param2,
|
||||
verify_args: Some(verify_args),
|
||||
args: vec![
|
||||
(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"),
|
||||
(ArgType::Node(false), "Node to set param2 values of"),
|
||||
(ArgType::Param2Val, "New param2 value")
|
||||
(ArgType::Area(false), "Area in which to set param2 values"),
|
||||
(ArgType::Invert, "Set param2 values *outside* the given area."),
|
||||
],
|
||||
help: "Set param2 value of certain nodes."
|
||||
help: "Set param2 values of certain nodes."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,6 @@ pub fn get_command() -> Command {
|
|||
func: vacuum,
|
||||
verify_args: None,
|
||||
args: Vec::new(),
|
||||
help: "Rebuild map database to reduce its size."
|
||||
help: "Rebuild the map database to reduce its size."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ pub enum ArgType {
|
|||
Item,
|
||||
Items,
|
||||
NewItem,
|
||||
Delete,
|
||||
DeleteMeta,
|
||||
DeleteItem,
|
||||
Key,
|
||||
Value,
|
||||
Param2Val,
|
||||
Param2,
|
||||
}
|
||||
|
||||
|
||||
|
@ -47,11 +47,11 @@ pub struct InstArgs {
|
|||
pub item: Option<String>,
|
||||
pub items: Option<Vec<String>>,
|
||||
pub new_item: Option<String>,
|
||||
pub delete: bool,
|
||||
pub delete_meta: bool,
|
||||
pub delete_item: bool,
|
||||
pub key: Option<String>,
|
||||
pub value: Option<String>,
|
||||
pub param2_val: Option<u8>,
|
||||
pub param2: Option<u8>,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ use std::cmp::min;
|
|||
use memmem::{Searcher, TwoWaySearcher};
|
||||
|
||||
|
||||
const END_STR: &[u8; 13] = b"EndInventory\n";
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeMetadata {
|
||||
pub vars: HashMap<Vec<u8>, (Vec<u8>, bool)>,
|
||||
|
@ -29,7 +32,6 @@ impl NodeMetadata {
|
|||
vars.insert(name.clone(), (val, private));
|
||||
}
|
||||
|
||||
const END_STR: &[u8; 13] = b"EndInventory\n";
|
||||
let end_finder = TwoWaySearcher::new(END_STR);
|
||||
let end = end_finder
|
||||
.search_in(&data.get_ref()[data.position() as usize ..])
|
||||
|
@ -54,6 +56,11 @@ impl NodeMetadata {
|
|||
|
||||
data.write_all(&self.inv).unwrap();
|
||||
}
|
||||
|
||||
/// Return `true` if the metadata contains no variables or inventory lists.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.vars.is_empty() && self.inv.starts_with(END_STR)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,6 +109,9 @@ impl NodeMetadataListExt for NodeMetadataList {
|
|||
data.write_u16::<BigEndian>(self.len() as u16).unwrap();
|
||||
|
||||
for (&pos, meta) in self {
|
||||
if meta.is_empty() {
|
||||
continue; // Skip empty metadata.
|
||||
}
|
||||
data.write_u16::<BigEndian>(pos).unwrap();
|
||||
meta.serialize(&mut data, version);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue