Fiddling, breaking stuff
This commit is contained in:
parent
b236c6d7d8
commit
2fd016bc22
56
Manual.md
56
Manual.md
@ -74,6 +74,22 @@ area.
|
||||
will be invoked where the blocks were deleted, and this sometimes causes
|
||||
terrain glitches.
|
||||
|
||||
### deleteobjects
|
||||
|
||||
Usage: `deleteobjects [--obj <obj>] [--items] [--p1 x y z] [--p2 x y z] [--invert]`
|
||||
|
||||
Delete objects (entities) of a certain name and/or within a certain area.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `--obj`: Name of object to search for, e.g. "boats:boat". If not specified,
|
||||
all objects will be deleted.
|
||||
- `--items`: Search for only item entities (dropped items). If this flag is
|
||||
set, `--obj` can optionally be used to specify an item name.
|
||||
- `--p1, --p2`: Area in which to delete objects. If not specified, objects will
|
||||
be deleted across the entire map.
|
||||
- `--invert`: Delete objects *outside* the given area.
|
||||
|
||||
### fill
|
||||
|
||||
Usage: `fill --p1 x y z --p2 x y z [--invert] <new_node>`
|
||||
@ -154,6 +170,21 @@ all nodes will be set.
|
||||
not specified.
|
||||
- `--invert`: Only set param2 *outside* the given area.
|
||||
|
||||
### vacuum
|
||||
|
||||
Usage: `vacuum`
|
||||
|
||||
Vacuums the database. This reduces the size of the database, but may take a
|
||||
long time.
|
||||
|
||||
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.
|
||||
|
||||
**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!
|
||||
|
||||
|
||||
|
||||
|
||||
@ -216,28 +247,3 @@ Arguments:
|
||||
- **`--searchnode`**: Name of node to search for. If not specified, the node timers of all nodes will be deleted.
|
||||
- **`--p1, --p2`**: Area in which to delete node timers. Required if `searchnode` is not specified.
|
||||
- **`--invert`**: Only delete node timers *outside* the given area.
|
||||
|
||||
### `deleteobjects`
|
||||
|
||||
**Usage:** `deleteobjects [--searchobj <searchobj>] [--items] [--p1 x y z] [--p2 x y z] [--invert]`
|
||||
|
||||
Delete static objects of a certain name and/or within a certain area.
|
||||
|
||||
Arguments:
|
||||
|
||||
- **`--searchobj`**: Name of object to search for, e.g. "boats:boat". If not specified, all objects will be deleted.
|
||||
- **`--items`**: Search for only item entities (dropped items). `searchobj` determines the item name, if specified.
|
||||
- **`--p1, --p2`**: Area in which to delete objects. If not specified, objects will be deleted across the entire map.
|
||||
- **`--invert`**: Only delete objects *outside* the given area.
|
||||
|
||||
### `vacuum`
|
||||
|
||||
**Usage:** `vacuum`
|
||||
|
||||
Vacuums the database. This reduces the size of the database, but may take a long time.
|
||||
|
||||
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.
|
||||
|
||||
**Note:** Because data is copied into another file, this command could require as much free disk space as is already occupied by the map.
|
||||
For example, if your database is 10 GB, make sure you have **at least 10 GB** of free space!
|
||||
|
@ -111,7 +111,7 @@ pub fn clean_name_id_map(block: &mut MapBlock) {
|
||||
}
|
||||
|
||||
// Rebuild the name-ID map.
|
||||
let mut new_nimap = BTreeMap::<u16, String>::new();
|
||||
let mut new_nimap = BTreeMap::<u16, Vec<u8>>::new();
|
||||
let mut map = vec![0u16; id_count];
|
||||
for id in 0 .. id_count {
|
||||
// Skip unused IDs.
|
||||
|
@ -1,7 +1,8 @@
|
||||
use super::Command;
|
||||
|
||||
use crate::spatial::Area;
|
||||
use crate::instance::{ArgType, InstBundle};
|
||||
use crate::map_block::{MapBlock, LuaEntityData};
|
||||
use crate::map_block::{MapBlock, StaticObject, LuaEntityData};
|
||||
use crate::utils::{query_keys, fmt_big_num};
|
||||
|
||||
use memmem::{Searcher, TwoWaySearcher};
|
||||
@ -17,21 +18,45 @@ macro_rules! unwrap_or {
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn can_delete(
|
||||
obj: &StaticObject,
|
||||
area: &Option<Area>,
|
||||
invert: bool
|
||||
) -> bool {
|
||||
// Check area requirements
|
||||
if let Some(a) = area {
|
||||
const DIV_FAC: i32 = 10_000;
|
||||
let rounded_pos = obj.f_pos.map(
|
||||
|v| (v - DIV_FAC / 2).div_euclid(DIV_FAC));
|
||||
if a.contains(rounded_pos) == invert {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
fn delete_objects(inst: &mut InstBundle) {
|
||||
const ITEM_ENT_NAME: &'static [u8] = b"__builtin:item";
|
||||
const ITEM_ENT_NAME: &[u8] = b"__builtin:item";
|
||||
let search_obj = if inst.args.items {
|
||||
Some(String::from_utf8(ITEM_ENT_NAME.to_vec()).unwrap())
|
||||
Some(ITEM_ENT_NAME.to_owned())
|
||||
} else {
|
||||
inst.args.object.clone()
|
||||
inst.args.object.as_ref().map(|s| s.as_bytes().to_owned())
|
||||
};
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
search_obj.clone(), inst.args.area, inst.args.invert, true);
|
||||
search_obj.as_deref(), inst.args.area, inst.args.invert, true);
|
||||
|
||||
let search_item = search_obj.as_ref().filter(|_| inst.args.items).map(|s|
|
||||
format!(
|
||||
"[\"itemstring\"] = \"{}\"",
|
||||
String::from_utf8(s.to_owned()).unwrap()
|
||||
).into_bytes()
|
||||
);
|
||||
let item_searcher = search_item.as_ref().map(|s| TwoWaySearcher::new(s));
|
||||
|
||||
inst.status.begin_editing();
|
||||
|
||||
let item_searcher = search_obj.as_ref().filter(|_| inst.args.items)
|
||||
.map(|s| TwoWaySearcher::new(format!("[itemstring]=\"{}\"", s)));
|
||||
|
||||
let mut count: u64 = 0;
|
||||
for key in keys {
|
||||
inst.status.inc_done();
|
||||
@ -42,39 +67,15 @@ fn delete_objects(inst: &mut InstBundle) {
|
||||
for i in (0..block.static_objects.list.len()).rev() {
|
||||
let obj = &block.static_objects.list[i];
|
||||
|
||||
// Check area requirements
|
||||
if let Some(area) = inst.args.area {
|
||||
const DIV_FAC: i32 = 10_000;
|
||||
let rounded_pos = obj.f_pos.map(
|
||||
|v| (v - DIV_FAC / 2).div_euclid(DIV_FAC));
|
||||
if area.contains(rounded_pos) == inst.args.invert {
|
||||
continue;
|
||||
}
|
||||
if can_delete(
|
||||
&block.static_objects.list[i],
|
||||
&inst.args.area,
|
||||
inst.args.invert
|
||||
) {
|
||||
block.static_objects.list.remove(i);
|
||||
modified = true;
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Check name requirements
|
||||
let le_data = unwrap_or!(LuaEntityData::deserialize(&obj),
|
||||
continue);
|
||||
if inst.args.items {
|
||||
if le_data.name != ITEM_ENT_NAME {
|
||||
continue;
|
||||
}
|
||||
if let Some(searcher) = &item_searcher {
|
||||
if searcher.search_in(&le_data.data).is_none() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(sobj) = &search_obj {
|
||||
if le_data.name != sobj.as_bytes() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block.static_objects.list.remove(i);
|
||||
modified = true;
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if modified {
|
||||
|
@ -23,7 +23,7 @@ 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.clone().unwrap();
|
||||
let node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
None, Some(area), false, true);
|
||||
|
@ -13,7 +13,7 @@ fn do_replace(
|
||||
block: &mut MapBlock,
|
||||
key: i64,
|
||||
search_id: u16,
|
||||
new_node: &str,
|
||||
new_node: &[u8],
|
||||
area: Option<Area>,
|
||||
invert: bool,
|
||||
tk: &mut TimeKeeper
|
||||
@ -113,10 +113,10 @@ fn do_replace(
|
||||
|
||||
|
||||
fn replace_nodes(inst: &mut InstBundle) {
|
||||
let node = inst.args.node.clone().unwrap();
|
||||
let new_node = inst.args.new_node.clone().unwrap();
|
||||
let node = inst.args.node.as_ref().unwrap().as_bytes();
|
||||
let new_node = inst.args.new_node.as_ref().unwrap().as_bytes();
|
||||
let keys = query_keys(&mut inst.db, &inst.status,
|
||||
Some(node.clone()), inst.args.area, inst.args.invert, true);
|
||||
Some(node), inst.args.area, inst.args.invert, true);
|
||||
|
||||
inst.status.begin_editing();
|
||||
let mut count = 0;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::Command;
|
||||
|
||||
use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block};
|
||||
use crate::instance::{ArgType, InstBundle};
|
||||
use crate::instance::{ArgType, InstArgs, InstBundle};
|
||||
use crate::map_block::{MapBlock};
|
||||
use crate::utils::{query_keys, fmt_big_num};
|
||||
|
||||
@ -42,12 +42,11 @@ fn set_in_area(block: &mut MapBlock, area: Area, val: u8) {
|
||||
|
||||
|
||||
fn set_param2(inst: &mut InstBundle) {
|
||||
// TODO: Actually verify!
|
||||
assert!(inst.args.area.is_some() || inst.args.node.is_some());
|
||||
let param2_val = inst.args.param2_val.unwrap();
|
||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
inst.args.node.clone(), inst.args.area, false, true);
|
||||
node.as_deref(), inst.args.area, false, true);
|
||||
|
||||
inst.status.begin_editing();
|
||||
|
||||
@ -59,8 +58,7 @@ fn set_param2(inst: &mut InstBundle) {
|
||||
let data = inst.db.get_block(key).unwrap();
|
||||
let mut block = MapBlock::deserialize(&data).unwrap();
|
||||
|
||||
let node_id = inst.args.node.as_deref()
|
||||
.and_then(|node| block.nimap.get_id(&node));
|
||||
let node_id = node.as_ref().and_then(|n| block.nimap.get_id(n));
|
||||
if inst.args.node.is_some() && node_id.is_none() {
|
||||
// Node not found in this map block.
|
||||
continue;
|
||||
@ -103,10 +101,17 @@ fn set_param2(inst: &mut InstBundle) {
|
||||
}
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
anyhow::ensure!(args.area.is_some() || args.node.is_some(),
|
||||
"An area and/or node must be provided.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn get_command() -> Command {
|
||||
Command {
|
||||
func: set_param2,
|
||||
verify_args: None,
|
||||
verify_args: Some(verify_args),
|
||||
args: vec![
|
||||
(ArgType::Area(false), "Area in which to set param2 values"),
|
||||
(ArgType::Node(false), "Node to set param2 values of"),
|
||||
|
@ -8,7 +8,7 @@ use super::*;
|
||||
#[derive(Debug)]
|
||||
pub struct NameIdMap {
|
||||
// Use a BTreeMap instead of a HashMap to preserve the order of IDs.
|
||||
pub map: BTreeMap<u16, String>,
|
||||
pub map: BTreeMap<u16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl NameIdMap {
|
||||
@ -26,8 +26,7 @@ impl NameIdMap {
|
||||
for _ in 0 .. count {
|
||||
let id = data.read_u16::<BigEndian>()?;
|
||||
let name = read_string16(data)?;
|
||||
let string = String::from_utf8_lossy(&name).into_owned();
|
||||
map.insert(id, string);
|
||||
map.insert(id, name);
|
||||
}
|
||||
|
||||
Ok(Self {map})
|
||||
@ -39,14 +38,15 @@ impl NameIdMap {
|
||||
|
||||
for (id, name) in &self.map {
|
||||
out.write_u16::<BigEndian>(*id).unwrap();
|
||||
write_string16(out, name.as_bytes());
|
||||
write_string16(out, name);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_id(&self, name: &str) -> Option<u16> {
|
||||
self.map.iter()
|
||||
.find_map(|(&k, v)| if v == name { Some(k) } else { None })
|
||||
pub fn get_id(&self, name: &[u8]) -> Option<u16> {
|
||||
self.map.iter().find_map(|(&k, v)|
|
||||
if v.as_slice() == name { Some(k) } else { None }
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -55,8 +55,8 @@ impl NameIdMap {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&mut self, id: u16, name: &str) {
|
||||
self.map.insert(id, name.to_string());
|
||||
pub fn insert(&mut self, id: u16, name: &[u8]) {
|
||||
self.map.insert(id, name.to_owned());
|
||||
}
|
||||
|
||||
/// Remove the name at a given ID and shift down values above it.
|
||||
|
@ -11,7 +11,7 @@ use crate::spatial::{Area, Vec3};
|
||||
pub fn query_keys(
|
||||
db: &mut MapDatabase,
|
||||
status: &StatusServer,
|
||||
search_str: Option<String>,
|
||||
search_str: Option<&[u8]>,
|
||||
area: Option<Area>,
|
||||
invert: bool,
|
||||
include_partial: bool
|
||||
@ -22,8 +22,8 @@ pub fn query_keys(
|
||||
// This will break if the name-ID map format changes.
|
||||
let search_bytes = search_str.map(|s| {
|
||||
let mut res = Vec::new();
|
||||
res.write_u16::<BigEndian>(s.as_bytes().len() as u16).unwrap();
|
||||
res.extend(s.as_bytes());
|
||||
res.write_u16::<BigEndian>(s.len() as u16).unwrap();
|
||||
res.extend(s);
|
||||
res
|
||||
});
|
||||
let data_searcher = search_bytes.as_ref().map(|b| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user