Work on multi-node support

master
random-geek 2021-01-28 11:58:58 -08:00
parent 0a4c759188
commit 98a0be45ec
14 changed files with 92 additions and 66 deletions

View File

@ -48,6 +48,7 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
.help(help)
];
}
// TODO: Help is redundant.
vec![match arg {
ArgType::InputMapPath =>
Arg::with_name("input_map")
@ -76,6 +77,11 @@ fn to_cmd_line_args<'a>(tup: &(ArgType, &'a str))
a
}
},
ArgType::Nodes =>
Arg::with_name("nodes")
.long("nodes")
.min_values(1)
.help(help),
ArgType::NewNode =>
Arg::with_name("new_node")
.takes_value(true)
@ -169,6 +175,8 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
offset: sub_matches.values_of("offset").map(arg_to_pos).transpose()
.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(),
new_node: sub_matches.value_of("new_node").map(str::to_string),
item: sub_matches.value_of("item").map(str::to_string),
new_item: sub_matches.value_of("new_item").map(str::to_string),

View File

@ -17,7 +17,7 @@ fn clone(inst: &mut InstBundle) {
let offset = inst.args.offset.unwrap();
let dst_area = src_area + offset;
let mut keys = query_keys(&mut inst.db, &inst.status,
Vec::new(), Some(dst_area), false, true);
&[], Some(dst_area), false, true);
// Sort blocks according to offset such that we don't read blocks that
// have already been written.

View File

@ -5,8 +5,8 @@ use crate::utils::query_keys;
fn delete_blocks(inst: &mut InstBundle) {
let keys = query_keys(&mut inst.db, &inst.status, Vec::new(),
inst.args.area, inst.args.invert, false);
let keys = query_keys(&mut inst.db, &inst.status,
&[], inst.args.area, inst.args.invert, false);
inst.status.begin_editing();
for key in keys {

View File

@ -4,14 +4,14 @@ use crate::unwrap_or;
use crate::spatial::Vec3;
use crate::instance::{ArgType, InstBundle};
use crate::map_block::{MapBlock, NodeMetadataList};
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, to_slice, fmt_big_num};
fn delete_metadata(inst: &mut InstBundle) {
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
let node = inst.args.node.as_ref().map(to_bytes);
let keys = query_keys(&mut inst.db, &mut inst.status,
node.iter().collect(), inst.args.area, inst.args.invert, true);
&to_slice(&node), inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut count: u64 = 0;

View File

@ -4,7 +4,7 @@ use crate::unwrap_or;
use crate::spatial::Area;
use crate::instance::{ArgType, InstBundle};
use crate::map_block::{MapBlock, StaticObject, LuaEntityData};
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, to_slice, fmt_big_num};
use memmem::{Searcher, TwoWaySearcher};
@ -54,19 +54,18 @@ fn delete_objects(inst: &mut InstBundle) {
let search_obj = if inst.args.items.is_some() {
Some(ITEM_ENT_NAME.to_owned())
} else {
inst.args.object.as_ref().map(|s| s.as_bytes().to_owned())
inst.args.object.as_ref().map(to_bytes)
};
// search_item will be Some if (1) item search is enabled and (2) an item
// is specified.
let search_item = inst.args.items.as_ref()
.and_then(|items| items.get(0))
.map(|s| s.as_bytes().to_owned());
let search_item = inst.args.items.as_ref().and_then(|items| items.get(0))
.map(to_bytes);
let item_searcher = search_item.as_ref()
.map(|s| TwoWaySearcher::new(s));
let keys = query_keys(&mut inst.db, &mut inst.status,
search_obj.iter().collect(), inst.args.area, inst.args.invert, true);
&to_slice(&search_obj), inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut count: u64 = 0;

View File

@ -4,14 +4,14 @@ use crate::unwrap_or;
use crate::spatial::Vec3;
use crate::instance::{ArgType, InstBundle};
use crate::map_block::MapBlock;
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, to_slice, fmt_big_num};
fn delete_timers(inst: &mut InstBundle) {
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
let node = inst.args.node.as_ref().map(to_bytes);
let keys = query_keys(&mut inst.db, &mut inst.status,
node.iter().collect(), inst.args.area, inst.args.invert, true);
&to_slice(&node), inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut count: u64 = 0;

View File

@ -4,7 +4,7 @@ use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block};
use crate::instance::{ArgType, InstBundle};
use crate::map_block::{MapBlock};
use crate::block_utils::clean_name_id_map;
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, fmt_big_num};
fn fill_area(block: &mut MapBlock, area: Area, id: u16) {
@ -23,10 +23,10 @@ fn fill_area(block: &mut MapBlock, area: Area, id: u16) {
fn fill(inst: &mut InstBundle) {
let area = inst.args.area.unwrap();
let node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
let node = to_bytes(inst.args.new_node.as_ref().unwrap());
let keys = query_keys(&mut inst.db, &mut inst.status,
Vec::new(), Some(area), false, true);
&[], Some(area), false, true);
inst.status.begin_editing();

View File

@ -31,7 +31,7 @@ fn overlay_no_offset(inst: &mut InstBundle) {
// Get keys from input database.
let keys = query_keys(&mut idb, &inst.status,
Vec::new(), inst.args.area, invert, true);
&[], inst.args.area, invert, true);
inst.status.begin_editing();
for key in keys {
@ -107,7 +107,7 @@ fn overlay_with_offset(inst: &mut InstBundle) {
// Get keys from output database.
let keys = query_keys(&mut inst.db, &inst.status,
Vec::new(), dst_area, inst.args.invert, true);
&[], dst_area, inst.args.invert, true);
inst.status.begin_editing();
for key in keys {

View File

@ -4,7 +4,7 @@ use crate::unwrap_or;
use crate::spatial::Vec3;
use crate::instance::{ArgType, InstBundle};
use crate::map_block::{MapBlock, NodeMetadataList};
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, fmt_big_num};
const NEWLINE: u8 = b'\n';
const SPACE: u8 = b' ';
@ -18,8 +18,12 @@ fn do_replace(inv: &mut Vec<u8>, item: &[u8], new_item: &[u8], del_meta: bool)
let mut mods = 0;
for line in inv.split(|&x| x == NEWLINE) {
let mut parts = line.splitn(4, |&x| x == SPACE);
if line.is_empty() {
// Necessary because of newline after final EndInventory
continue;
}
let mut parts = line.splitn(4, |&x| x == SPACE);
if parts.next() == Some(b"Item") && parts.next() == Some(item) {
if delete {
new_inv.extend_from_slice(b"Empty");
@ -53,13 +57,13 @@ fn do_replace(inv: &mut Vec<u8>, item: &[u8], new_item: &[u8], del_meta: bool)
fn replace_in_inv(inst: &mut InstBundle) {
let item = inst.args.item.as_ref().unwrap().as_bytes().to_owned();
let new_item = inst.args.new_item.as_ref().unwrap().as_bytes().to_owned();
let item = to_bytes(inst.args.item.as_ref().unwrap());
let new_item = to_bytes(inst.args.new_item.as_ref().unwrap());
let del_meta = false;
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
let nodes: Vec<_> = inst.args.nodes.iter().map(to_bytes).collect();
let keys = query_keys(&mut inst.db, &mut inst.status,
node.iter().collect(), inst.args.area, inst.args.invert, true);
&nodes, inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut item_mods: u64 = 0;
@ -71,9 +75,10 @@ fn replace_in_inv(inst: &mut InstBundle) {
let mut block = unwrap_or!(MapBlock::deserialize(&data), continue);
let node_data = block.node_data.get_ref();
let node_id = node.as_deref().and_then(|n| block.nimap.get_id(n));
if node.is_some() && node_id.is_none() {
continue; // Block doesn't contain the required node.
let node_ids: Vec<_> = nodes.iter()
.filter_map(|n| block.nimap.get_id(n)).collect();
if !nodes.is_empty() && node_ids.is_empty() {
continue; // Block doesn't contain any of the required nodes.
}
let mut meta = unwrap_or!(
@ -90,10 +95,10 @@ fn replace_in_inv(inst: &mut InstBundle) {
continue;
}
}
if let Some(id) = node_id {
if node_data.nodes[idx as usize] != id {
continue;
}
if !node_ids.is_empty()
&& !node_ids.contains(&node_data.nodes[idx as usize])
{
continue;
}
let i_mods = do_replace(&mut data.inv, &item, &new_item, del_meta);
@ -125,7 +130,7 @@ pub fn get_command() -> Command {
(ArgType::NewItem, "Name of new item to replace with"),
(ArgType::Area(false), "Area in which to modify inventories"),
(ArgType::Invert, "Modify inventories outside the given area."),
(ArgType::Node(false), "Node to modify inventories of")
(ArgType::Nodes, "Names of nodes to modify inventories of")
],
help: "Replace items in node inventories."
}

View File

@ -4,9 +4,8 @@ use crate::spatial::{Vec3, Area, area_contains_block, area_touches_block,
area_rel_block_overlap};
use crate::instance::{ArgType, InstArgs, InstBundle};
use crate::map_block::MapBlock;
use crate::utils::query_keys;
use crate::time_keeper::TimeKeeper;
use crate::utils::fmt_big_num;
use crate::utils::{query_keys, to_bytes, fmt_big_num};
fn do_replace(
@ -113,10 +112,10 @@ fn do_replace(
fn replace_nodes(inst: &mut InstBundle) {
let node = inst.args.node.as_ref().unwrap().as_bytes().to_owned();
let new_node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
let node = to_bytes(inst.args.node.as_ref().unwrap());
let new_node = to_bytes(inst.args.new_node.as_ref().unwrap());
let keys = query_keys(&mut inst.db, &inst.status,
vec![&node], inst.args.area, inst.args.invert, true);
std::slice::from_ref(&node), inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut count = 0;

View File

@ -4,16 +4,16 @@ use crate::unwrap_or;
use crate::spatial::Vec3;
use crate::instance::{ArgType, InstBundle};
use crate::map_block::{MapBlock, NodeMetadataList};
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, fmt_big_num};
fn set_meta_var(inst: &mut InstBundle) {
let key = inst.args.key.as_ref().unwrap().as_bytes().to_owned();
let value = inst.args.value.as_ref().unwrap().as_bytes().to_owned();
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
let key = to_bytes(inst.args.key.as_ref().unwrap());
let value = to_bytes(inst.args.value.as_ref().unwrap());
let nodes: Vec<_> = inst.args.nodes.iter().map(to_bytes).collect();
let keys = query_keys(&mut inst.db, &mut inst.status,
node.iter().collect(), inst.args.area, inst.args.invert, true);
&nodes, inst.args.area, inst.args.invert, true);
inst.status.begin_editing();
let mut count: u64 = 0;
@ -24,9 +24,10 @@ fn set_meta_var(inst: &mut InstBundle) {
let mut block = unwrap_or!(MapBlock::deserialize(&data), continue);
let node_data = block.node_data.get_ref();
let node_id = node.as_deref().and_then(|n| block.nimap.get_id(n));
if node.is_some() && node_id.is_none() {
continue; // Block doesn't contain the required node.
let node_ids: Vec<_> = nodes.iter()
.filter_map(|n| block.nimap.get_id(n)).collect();
if !nodes.is_empty() && node_ids.is_empty() {
continue; // Block doesn't contain any of the required nodes.
}
let mut meta = unwrap_or!(
@ -44,10 +45,10 @@ fn set_meta_var(inst: &mut InstBundle) {
continue;
}
}
if let Some(id) = node_id {
if node_data.nodes[idx as usize] != id {
continue;
}
if !node_ids.is_empty()
&& !node_ids.contains(&node_data.nodes[idx as usize])
{
continue;
}
if let Some(val) = data.vars.get_mut(&key) {
@ -76,11 +77,12 @@ pub fn get_command() -> Command {
args: vec![
(ArgType::Key, "Name of key to set in metadata"),
(ArgType::Value, "Value to set in metadata"),
(ArgType::Area(false), "Area in which to modify node metadata"),
(ArgType::Area(false),
"Optional area in which to modify node metadata"),
(ArgType::Invert, "Modify node metadata outside the given area."),
(ArgType::Node(false),
"Node to modify metadata in. If not specified, all relevant \
metadata will be modified.")
(ArgType::Nodes,
"Names of one or more nodes to modify. If not specified, all \
nodes with the specified variable will be modified.")
],
help: "Set a variable in node metadata."
}

View File

@ -3,7 +3,7 @@ use super::Command;
use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block};
use crate::instance::{ArgType, InstArgs, InstBundle};
use crate::map_block::{MapBlock};
use crate::utils::{query_keys, fmt_big_num};
use crate::utils::{query_keys, to_bytes, to_slice, fmt_big_num};
fn set_in_area_node(block: &mut MapBlock, area: Area, id: u16, val: u8) -> u64
@ -43,10 +43,10 @@ fn set_in_area(block: &mut MapBlock, area: Area, val: u8) {
fn set_param2(inst: &mut InstBundle) {
let param2_val = inst.args.param2_val.unwrap();
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
let node = inst.args.node.as_ref().map(to_bytes);
let keys = query_keys(&mut inst.db, &mut inst.status,
node.iter().collect(), inst.args.area, false, true);
to_slice(&node), inst.args.area, false, true);
inst.status.begin_editing();

View File

@ -16,6 +16,7 @@ pub enum ArgType {
Invert,
Offset(bool),
Node(bool),
Nodes,
NewNode,
Item,
NewItem,
@ -36,6 +37,7 @@ pub struct InstArgs {
pub invert: bool,
pub offset: Option<Vec3>,
pub node: Option<String>,
pub nodes: Vec<String>,
pub new_node: Option<String>,
pub item: Option<String>,
pub new_item: Option<String>,

View File

@ -11,8 +11,7 @@ use crate::spatial::{Area, Vec3};
pub fn query_keys(
db: &mut MapDatabase,
status: &StatusServer,
// TODO: Allow multiple names for setmetavar and replaceininv.
search_strs: Vec<&Vec<u8>>,
search_strs: &[Vec<u8>],
area: Option<Area>,
invert: bool,
include_partial: bool
@ -21,7 +20,7 @@ pub fn query_keys(
// Prepend 16-bit search string length to reduce false positives.
// This will break if the name-ID map format changes.
let string16s: Vec<Vec<u8>> = search_strs.iter().map(|&s| {
let string16s: Vec<Vec<u8>> = search_strs.iter().map(|s| {
let mut res = Vec::new();
res.write_u16::<BigEndian>(s.len() as u16).unwrap();
res.extend(s);
@ -49,11 +48,10 @@ pub fn query_keys(
continue;
}
}
if !data_searchers.is_empty() {
// Data must match at least one search string.
if data_searchers.iter().any(|s| s.search_in(&data).is_some()) {
continue;
}
if !data_searchers.is_empty()
&& !data_searchers.iter().any(|s| s.search_in(&data).is_some())
{ // Data must match at least one search string.
continue;
}
keys.push(key);
@ -69,6 +67,19 @@ pub fn query_keys(
}
pub fn to_bytes(s: &String) -> Vec<u8> {
s.as_bytes().to_vec()
}
pub fn to_slice(opt: &Option<Vec<u8>>) -> &[Vec<u8>] {
match opt {
Some(x) => std::slice::from_ref(x),
None => &[]
}
}
#[macro_export]
macro_rules! unwrap_or {
($res:expr, $alt:expr) => {