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
|
will be invoked where the blocks were deleted, and this sometimes causes
|
||||||
terrain glitches.
|
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
|
### fill
|
||||||
|
|
||||||
Usage: `fill --p1 x y z --p2 x y z [--invert] <new_node>`
|
Usage: `fill --p1 x y z --p2 x y z [--invert] <new_node>`
|
||||||
@ -154,6 +170,21 @@ all nodes will be set.
|
|||||||
not specified.
|
not specified.
|
||||||
- `--invert`: Only set param2 *outside* the given area.
|
- `--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.
|
- **`--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.
|
- **`--p1, --p2`**: Area in which to delete node timers. Required if `searchnode` is not specified.
|
||||||
- **`--invert`**: Only delete node timers *outside* the given area.
|
- **`--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.
|
// 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];
|
let mut map = vec![0u16; id_count];
|
||||||
for id in 0 .. id_count {
|
for id in 0 .. id_count {
|
||||||
// Skip unused IDs.
|
// Skip unused IDs.
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use super::Command;
|
use super::Command;
|
||||||
|
|
||||||
|
use crate::spatial::Area;
|
||||||
use crate::instance::{ArgType, InstBundle};
|
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 crate::utils::{query_keys, fmt_big_num};
|
||||||
|
|
||||||
use memmem::{Searcher, TwoWaySearcher};
|
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) {
|
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 {
|
let search_obj = if inst.args.items {
|
||||||
Some(String::from_utf8(ITEM_ENT_NAME.to_vec()).unwrap())
|
Some(ITEM_ENT_NAME.to_owned())
|
||||||
} else {
|
} 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,
|
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();
|
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;
|
let mut count: u64 = 0;
|
||||||
for key in keys {
|
for key in keys {
|
||||||
inst.status.inc_done();
|
inst.status.inc_done();
|
||||||
@ -42,40 +67,16 @@ fn delete_objects(inst: &mut InstBundle) {
|
|||||||
for i in (0..block.static_objects.list.len()).rev() {
|
for i in (0..block.static_objects.list.len()).rev() {
|
||||||
let obj = &block.static_objects.list[i];
|
let obj = &block.static_objects.list[i];
|
||||||
|
|
||||||
// Check area requirements
|
if can_delete(
|
||||||
if let Some(area) = inst.args.area {
|
&block.static_objects.list[i],
|
||||||
const DIV_FAC: i32 = 10_000;
|
&inst.args.area,
|
||||||
let rounded_pos = obj.f_pos.map(
|
inst.args.invert
|
||||||
|v| (v - DIV_FAC / 2).div_euclid(DIV_FAC));
|
) {
|
||||||
if area.contains(rounded_pos) == inst.args.invert {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
block.static_objects.list.remove(i);
|
||||||
modified = true;
|
modified = true;
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if modified {
|
if modified {
|
||||||
inst.db.set_block(key, &block.serialize()).unwrap();
|
inst.db.set_block(key, &block.serialize()).unwrap();
|
||||||
|
@ -23,7 +23,7 @@ fn fill_area(block: &mut MapBlock, area: Area, id: u16) {
|
|||||||
|
|
||||||
fn fill(inst: &mut InstBundle) {
|
fn fill(inst: &mut InstBundle) {
|
||||||
let area = inst.args.area.unwrap();
|
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,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
None, Some(area), false, true);
|
None, Some(area), false, true);
|
||||||
|
@ -13,7 +13,7 @@ fn do_replace(
|
|||||||
block: &mut MapBlock,
|
block: &mut MapBlock,
|
||||||
key: i64,
|
key: i64,
|
||||||
search_id: u16,
|
search_id: u16,
|
||||||
new_node: &str,
|
new_node: &[u8],
|
||||||
area: Option<Area>,
|
area: Option<Area>,
|
||||||
invert: bool,
|
invert: bool,
|
||||||
tk: &mut TimeKeeper
|
tk: &mut TimeKeeper
|
||||||
@ -113,10 +113,10 @@ fn do_replace(
|
|||||||
|
|
||||||
|
|
||||||
fn replace_nodes(inst: &mut InstBundle) {
|
fn replace_nodes(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.clone().unwrap();
|
let node = inst.args.node.as_ref().unwrap().as_bytes();
|
||||||
let new_node = inst.args.new_node.clone().unwrap();
|
let new_node = inst.args.new_node.as_ref().unwrap().as_bytes();
|
||||||
let keys = query_keys(&mut inst.db, &inst.status,
|
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();
|
inst.status.begin_editing();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::Command;
|
use super::Command;
|
||||||
|
|
||||||
use crate::spatial::{Vec3, Area, area_rel_block_overlap, area_contains_block};
|
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::map_block::{MapBlock};
|
||||||
use crate::utils::{query_keys, fmt_big_num};
|
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) {
|
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 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,
|
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();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
@ -59,8 +58,7 @@ fn set_param2(inst: &mut InstBundle) {
|
|||||||
let data = inst.db.get_block(key).unwrap();
|
let data = inst.db.get_block(key).unwrap();
|
||||||
let mut block = MapBlock::deserialize(&data).unwrap();
|
let mut block = MapBlock::deserialize(&data).unwrap();
|
||||||
|
|
||||||
let node_id = inst.args.node.as_deref()
|
let node_id = node.as_ref().and_then(|n| block.nimap.get_id(n));
|
||||||
.and_then(|node| block.nimap.get_id(&node));
|
|
||||||
if inst.args.node.is_some() && node_id.is_none() {
|
if inst.args.node.is_some() && node_id.is_none() {
|
||||||
// Node not found in this map block.
|
// Node not found in this map block.
|
||||||
continue;
|
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 {
|
pub fn get_command() -> Command {
|
||||||
Command {
|
Command {
|
||||||
func: set_param2,
|
func: set_param2,
|
||||||
verify_args: None,
|
verify_args: Some(verify_args),
|
||||||
args: vec![
|
args: vec![
|
||||||
(ArgType::Area(false), "Area in which to set param2 values"),
|
(ArgType::Area(false), "Area in which to set param2 values"),
|
||||||
(ArgType::Node(false), "Node to set param2 values of"),
|
(ArgType::Node(false), "Node to set param2 values of"),
|
||||||
|
@ -8,7 +8,7 @@ use super::*;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NameIdMap {
|
pub struct NameIdMap {
|
||||||
// Use a BTreeMap instead of a HashMap to preserve the order of IDs.
|
// 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 {
|
impl NameIdMap {
|
||||||
@ -26,8 +26,7 @@ impl NameIdMap {
|
|||||||
for _ in 0 .. count {
|
for _ in 0 .. count {
|
||||||
let id = data.read_u16::<BigEndian>()?;
|
let id = data.read_u16::<BigEndian>()?;
|
||||||
let name = read_string16(data)?;
|
let name = read_string16(data)?;
|
||||||
let string = String::from_utf8_lossy(&name).into_owned();
|
map.insert(id, name);
|
||||||
map.insert(id, string);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {map})
|
Ok(Self {map})
|
||||||
@ -39,14 +38,15 @@ impl NameIdMap {
|
|||||||
|
|
||||||
for (id, name) in &self.map {
|
for (id, name) in &self.map {
|
||||||
out.write_u16::<BigEndian>(*id).unwrap();
|
out.write_u16::<BigEndian>(*id).unwrap();
|
||||||
write_string16(out, name.as_bytes());
|
write_string16(out, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_id(&self, name: &str) -> Option<u16> {
|
pub fn get_id(&self, name: &[u8]) -> Option<u16> {
|
||||||
self.map.iter()
|
self.map.iter().find_map(|(&k, v)|
|
||||||
.find_map(|(&k, v)| if v == name { Some(k) } else { None })
|
if v.as_slice() == name { Some(k) } else { None }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -55,8 +55,8 @@ impl NameIdMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert(&mut self, id: u16, name: &str) {
|
pub fn insert(&mut self, id: u16, name: &[u8]) {
|
||||||
self.map.insert(id, name.to_string());
|
self.map.insert(id, name.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the name at a given ID and shift down values above it.
|
/// 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(
|
pub fn query_keys(
|
||||||
db: &mut MapDatabase,
|
db: &mut MapDatabase,
|
||||||
status: &StatusServer,
|
status: &StatusServer,
|
||||||
search_str: Option<String>,
|
search_str: Option<&[u8]>,
|
||||||
area: Option<Area>,
|
area: Option<Area>,
|
||||||
invert: bool,
|
invert: bool,
|
||||||
include_partial: bool
|
include_partial: bool
|
||||||
@ -22,8 +22,8 @@ pub fn query_keys(
|
|||||||
// This will break if the name-ID map format changes.
|
// This will break if the name-ID map format changes.
|
||||||
let search_bytes = search_str.map(|s| {
|
let search_bytes = search_str.map(|s| {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
res.write_u16::<BigEndian>(s.as_bytes().len() as u16).unwrap();
|
res.write_u16::<BigEndian>(s.len() as u16).unwrap();
|
||||||
res.extend(s.as_bytes());
|
res.extend(s);
|
||||||
res
|
res
|
||||||
});
|
});
|
||||||
let data_searcher = search_bytes.as_ref().map(|b| {
|
let data_searcher = search_bytes.as_ref().map(|b| {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user