Completion Part 2
This commit is contained in:
parent
a593f141ff
commit
b1f3f66006
19
Manual.md
19
Manual.md
@ -20,11 +20,12 @@ SQLite format maps are currently supported.
|
||||
|
||||
## General usage
|
||||
|
||||
`mapeditr [-h] <map> <subcommand>`
|
||||
`mapeditr [-h] [-y] <map> <subcommand>`
|
||||
|
||||
Arguments:
|
||||
|
||||
- `-h`: Show a help message and exit.
|
||||
- `-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.
|
||||
@ -75,8 +76,8 @@ Arguments:
|
||||
|
||||
- `--p1, --p2`: Area containing mapblocks to delete. By default, only mapblocks
|
||||
fully within this area will be deleted.
|
||||
- `--invert`: Delete all mapblocks fully *outside* the given area. Use with
|
||||
caution; you could erase a large portion of your world!
|
||||
- `--invert`: If present, 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
|
||||
@ -91,11 +92,11 @@ contents) are also deleted.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `--node`: Only delete metadata of nodes with the given name. If not
|
||||
specified, metadata will be deleted in all matching nodes.
|
||||
- `--p1, --p2`: Area in which to delete metadata. If not specified, metadata
|
||||
will be deleted everywhere.
|
||||
- `--invert`: Only delete metadata *outside* the given area.
|
||||
- `--node`: (Optional) Name of node to delete metadata from. If not 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.
|
||||
|
||||
### deleteobjects
|
||||
|
||||
|
@ -139,6 +139,12 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
||||
For additional information, see the manual.")
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!())
|
||||
.arg(Arg::with_name("yes")
|
||||
.long("yes")
|
||||
.short("y")
|
||||
.global(true)
|
||||
.help("Skip the default confirmation prompt.")
|
||||
)
|
||||
// TODO: Move map arg to subcommands?
|
||||
.arg(Arg::with_name("map")
|
||||
.required(true)
|
||||
@ -153,6 +159,7 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
||||
let sub_matches = matches.subcommand_matches(&sub_name).unwrap();
|
||||
|
||||
Ok(InstArgs {
|
||||
do_confirmation: !matches.is_present("yes"),
|
||||
command: sub_name,
|
||||
map_path: matches.value_of("map").unwrap().to_string(),
|
||||
input_map_path: sub_matches.value_of("input_map").map(str::to_string),
|
||||
@ -214,7 +221,7 @@ fn print_editing_status(done: usize, total: usize, real_start: Instant,
|
||||
let num_bars = (progress * TOTAL_BARS as f32) as usize;
|
||||
let bars = "=".repeat(num_bars);
|
||||
|
||||
eprint!(
|
||||
print!(
|
||||
"\r[{bars:<total_bars$}] {progress:.1}% | {elapsed} elapsed \
|
||||
| {remaining} remaining",
|
||||
bars=bars,
|
||||
@ -228,7 +235,7 @@ fn print_editing_status(done: usize, total: usize, real_start: Instant,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
eprint!("\rProcessing... {} elapsed", fmt_duration(real_elapsed));
|
||||
print!("\rProcessing... {} elapsed", fmt_duration(real_elapsed));
|
||||
}
|
||||
|
||||
std::io::stdout().flush().unwrap();
|
||||
@ -239,13 +246,21 @@ fn print_log(log_type: LogType, msg: String) {
|
||||
let prefix = format!("{}: ", log_type);
|
||||
let indented = msg.lines().collect::<Vec<_>>()
|
||||
.join(&format!( "\n{}", " ".repeat(prefix.len()) ));
|
||||
eprintln!("{}{}", prefix, indented);
|
||||
println!("{}{}", prefix, indented);
|
||||
}
|
||||
|
||||
|
||||
fn get_confirmation() -> bool {
|
||||
print!("Proceed? (Y/n): ");
|
||||
let mut result = String::new();
|
||||
std::io::stdin().read_line(&mut result).unwrap();
|
||||
result.trim().to_ascii_lowercase() == "y"
|
||||
}
|
||||
|
||||
|
||||
pub fn run_cmd_line() {
|
||||
use std::sync::mpsc;
|
||||
use crate::instance::{InstState, InstEvent, spawn_compute_thread};
|
||||
use crate::instance::{InstState, ServerEvent, spawn_compute_thread};
|
||||
|
||||
let args = match parse_cmd_line_args() {
|
||||
Ok(a) => a,
|
||||
@ -265,13 +280,24 @@ pub fn run_cmd_line() {
|
||||
let mut cur_state = InstState::Ignore;
|
||||
let mut need_newline = false;
|
||||
|
||||
let newline_if = |condition: &mut bool| {
|
||||
if *condition {
|
||||
println!();
|
||||
*condition = false;
|
||||
}
|
||||
};
|
||||
|
||||
loop { /* Main command-line logging loop */
|
||||
let now = Instant::now();
|
||||
let mut forced_update = InstState::Ignore;
|
||||
|
||||
match status.event_rx.recv_timeout(TICK) {
|
||||
match status.receiver().recv_timeout(TICK) {
|
||||
Ok(event) => match event {
|
||||
InstEvent::NewState(new_state) => {
|
||||
ServerEvent::Log(log_type, msg) => {
|
||||
newline_if(&mut need_newline);
|
||||
print_log(log_type, msg);
|
||||
},
|
||||
ServerEvent::NewState(new_state) => {
|
||||
// Force progress updates at the beginning and end of
|
||||
// querying/editing stages.
|
||||
if (cur_state == InstState::Ignore) !=
|
||||
@ -290,13 +316,10 @@ pub fn run_cmd_line() {
|
||||
}
|
||||
cur_state = new_state;
|
||||
},
|
||||
InstEvent::Log(log_type, msg) => {
|
||||
if need_newline {
|
||||
eprintln!();
|
||||
need_newline = false;
|
||||
}
|
||||
print_log(log_type, msg);
|
||||
}
|
||||
ServerEvent::ConfirmRequest => {
|
||||
newline_if(&mut need_newline);
|
||||
status.confirm(get_confirmation());
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
// Compute thread has exited; break out of the loop.
|
||||
@ -311,8 +334,8 @@ pub fn run_cmd_line() {
|
||||
if forced_update == InstState::Querying
|
||||
|| (cur_state == InstState::Querying && timed_update_ready)
|
||||
{
|
||||
eprint!("\rQuerying mapblocks... {} found.",
|
||||
status.get().blocks_total);
|
||||
print!("\rQuerying mapblocks... {} found.",
|
||||
status.get_status().blocks_total);
|
||||
std::io::stdout().flush().unwrap();
|
||||
last_update = now;
|
||||
need_newline = true;
|
||||
@ -320,8 +343,7 @@ pub fn run_cmd_line() {
|
||||
else if forced_update == InstState::Editing
|
||||
|| (cur_state == InstState::Editing && timed_update_ready)
|
||||
{
|
||||
let s = status.get();
|
||||
// TODO: Update duration format? e.g. 1m 42s remaining
|
||||
let s = status.get_status();
|
||||
print_editing_status(s.blocks_done, s.blocks_total,
|
||||
querying_start, editing_start, s.show_progress);
|
||||
last_update = now;
|
||||
@ -329,9 +351,8 @@ pub fn run_cmd_line() {
|
||||
}
|
||||
|
||||
// Print a newline after the last querying/editing message.
|
||||
if need_newline && cur_state == InstState::Ignore {
|
||||
eprintln!();
|
||||
need_newline = false;
|
||||
if cur_state == InstState::Ignore {
|
||||
newline_if(&mut need_newline);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{Command, BLOCK_CACHE_SIZE};
|
||||
use super::{Command, ArgResult, BLOCK_CACHE_SIZE};
|
||||
|
||||
use crate::{unwrap_or, opt_unwrap_or};
|
||||
use crate::spatial::{Vec3, Area, MAP_LIMIT};
|
||||
@ -10,7 +10,7 @@ use crate::instance::{ArgType, InstBundle, InstArgs};
|
||||
use crate::utils::{CacheMap, query_keys};
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
let map_area = Area::new(
|
||||
Vec3::new(-MAP_LIMIT, -MAP_LIMIT, -MAP_LIMIT),
|
||||
Vec3::new(MAP_LIMIT, MAP_LIMIT, MAP_LIMIT)
|
||||
@ -19,10 +19,10 @@ fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
if map_area.intersection(args.area.unwrap() + args.offset.unwrap())
|
||||
.is_none()
|
||||
{
|
||||
anyhow::bail!("Destination area is outside map bounds.");
|
||||
return ArgResult::error("Destination area is outside map bounds.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,7 +25,8 @@ pub fn get_command() -> Command {
|
||||
args: vec![
|
||||
(ArgType::Area(true), "Area containing mapblocks to delete"),
|
||||
(ArgType::Invert,
|
||||
"Delete all mapblocks fully *outside* the given area.")
|
||||
"If present, delete all mapblocks fully *outside* the given \
|
||||
area.")
|
||||
],
|
||||
help: "Delete all mapblocks inside or outside an area."
|
||||
}
|
||||
|
@ -1,12 +1,22 @@
|
||||
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, to_slice, fmt_big_num};
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if !args.area.is_some() && !args.node.is_some() {
|
||||
return ArgResult::warning(
|
||||
"No area or node specified. ALL metadata will be deleted!");
|
||||
}
|
||||
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
fn delete_metadata(inst: &mut InstBundle) {
|
||||
let node = inst.args.node.as_ref().map(to_bytes);
|
||||
|
||||
@ -71,14 +81,13 @@ fn delete_metadata(inst: &mut InstBundle) {
|
||||
pub fn get_command() -> Command {
|
||||
Command {
|
||||
func: delete_metadata,
|
||||
verify_args: None,
|
||||
verify_args: Some(verify_args),
|
||||
args: vec![
|
||||
(ArgType::Area(false), "Area in which to delete metadata"),
|
||||
(ArgType::Invert, "Delete all metadata outside the given area."),
|
||||
(ArgType::Node(false),
|
||||
"Node to delete metadata from. If not specified, all metadata \
|
||||
will be deleted.")
|
||||
(ArgType::Invert,
|
||||
"If present, delete metadata *outside* the given area."),
|
||||
(ArgType::Node(false), "Name of node to delete metadata from")
|
||||
],
|
||||
help: "Delete node metadata."
|
||||
help: "Delete node metadata of certain nodes."
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,7 @@ fn delete_objects(inst: &mut InstBundle) {
|
||||
// is specified.
|
||||
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 item_searcher = search_item.as_ref().map(|s| TwoWaySearcher::new(s));
|
||||
|
||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||
to_slice(&search_obj), inst.args.area, inst.args.invert, true);
|
||||
@ -71,8 +70,9 @@ fn delete_objects(inst: &mut InstBundle) {
|
||||
let mut count: u64 = 0;
|
||||
for key in keys {
|
||||
inst.status.inc_done();
|
||||
let data = unwrap_or!(inst.db.get_block(key), continue);
|
||||
let mut block = unwrap_or!(MapBlock::deserialize(&data), continue);
|
||||
let data = inst.db.get_block(key).unwrap();
|
||||
let mut block = unwrap_or!(MapBlock::deserialize(&data),
|
||||
{ inst.status.inc_failed(); continue; });
|
||||
|
||||
let mut modified = false;
|
||||
for i in (0..block.static_objects.len()).rev() {
|
||||
|
@ -19,9 +19,30 @@ mod vacuum;
|
||||
pub const BLOCK_CACHE_SIZE: usize = 1024;
|
||||
|
||||
|
||||
pub enum ArgResult {
|
||||
Ok,
|
||||
Warning(String),
|
||||
Error(String),
|
||||
}
|
||||
|
||||
impl ArgResult {
|
||||
/// Create a new ArgResult::Warning from a &str.
|
||||
#[inline]
|
||||
pub fn warning(msg: &str) -> Self {
|
||||
Self::Warning(msg.to_string())
|
||||
}
|
||||
|
||||
/// Create a new ArgResult::Error from a &str.
|
||||
#[inline]
|
||||
pub fn error(msg: &str) -> Self {
|
||||
Self::Error(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Command {
|
||||
pub func: fn(&mut InstBundle),
|
||||
pub verify_args: Option<fn(&InstArgs) -> anyhow::Result<()>>,
|
||||
pub verify_args: Option<fn(&InstArgs) -> ArgResult>,
|
||||
pub help: &'static str,
|
||||
pub args: Vec<(ArgType, &'static str)>
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{Command, BLOCK_CACHE_SIZE};
|
||||
use super::{Command, ArgResult, BLOCK_CACHE_SIZE};
|
||||
|
||||
use crate::{unwrap_or, opt_unwrap_or};
|
||||
use crate::spatial::{Vec3, Area, MAP_LIMIT};
|
||||
@ -10,11 +10,11 @@ use crate::block_utils::{merge_blocks, merge_metadata, clean_name_id_map};
|
||||
use crate::utils::{query_keys, CacheMap};
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.invert
|
||||
&& args.offset.filter(|&ofs| ofs != Vec3::new(0, 0, 0)).is_some()
|
||||
{
|
||||
anyhow::bail!("Inverted selections cannot be offset.");
|
||||
return ArgResult::error("Inverted selections cannot be offset.");
|
||||
}
|
||||
|
||||
let offset = args.offset.unwrap_or(Vec3::new(0, 0, 0));
|
||||
@ -26,10 +26,10 @@ fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
if map_area.intersection(args.area.unwrap_or(map_area) + offset)
|
||||
.is_none()
|
||||
{
|
||||
anyhow::bail!("Destination area is outside map bounds.");
|
||||
return ArgResult::error("Destination area is outside map bounds.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::Command;
|
||||
use super::{Command, ArgResult};
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::Vec3;
|
||||
@ -129,14 +129,15 @@ fn replace_in_inv(inst: &mut InstBundle) {
|
||||
}
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.new_item.is_none() && !args.delete_item && !args.delete_meta {
|
||||
anyhow::bail!(
|
||||
"new_item is required unless --delete or --deletemeta is used.")
|
||||
return ArgResult::error(
|
||||
"new_item is required unless --delete or --deletemeta is used.");
|
||||
} else if args.new_item.is_some() && args.delete_item {
|
||||
anyhow::bail!("Cannot delete items if new_item is specified.");
|
||||
return ArgResult::error(
|
||||
"Cannot delete items if new_item is specified.");
|
||||
}
|
||||
Ok(())
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::Command;
|
||||
use super::{Command, ArgResult};
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::{Vec3, Area, InverseBlockIterator};
|
||||
@ -127,10 +127,12 @@ fn replace_nodes(inst: &mut InstBundle) {
|
||||
}
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
anyhow::ensure!(args.node != args.new_node,
|
||||
"node and new_node must be different.");
|
||||
Ok(())
|
||||
fn verify_args(args: &InstArgs) -> ArgResult {
|
||||
if args.node == args.new_node {
|
||||
return ArgResult::error("node and new_node must be different.");
|
||||
}
|
||||
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::Command;
|
||||
use super::{Command, ArgResult};
|
||||
|
||||
use crate::unwrap_or;
|
||||
use crate::spatial::{Vec3, Area, InverseBlockIterator};
|
||||
@ -107,10 +107,12 @@ 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(())
|
||||
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.");
|
||||
}
|
||||
|
||||
ArgResult::Ok
|
||||
}
|
||||
|
||||
|
||||
|
127
src/instance.rs
127
src/instance.rs
@ -7,6 +7,7 @@ use anyhow::Context;
|
||||
use crate::spatial::{Vec3, Area, MAP_LIMIT};
|
||||
use crate::map_database::MapDatabase;
|
||||
use crate::commands;
|
||||
use crate::commands::ArgResult;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -32,6 +33,7 @@ pub enum ArgType {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstArgs {
|
||||
pub do_confirmation: bool,
|
||||
pub command: String,
|
||||
pub map_path: String,
|
||||
pub input_map_path: Option<String>,
|
||||
@ -86,6 +88,7 @@ impl InstStatus {
|
||||
|
||||
pub enum LogType {
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
@ -93,22 +96,29 @@ impl std::fmt::Display for LogType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Info => write!(f, "info"),
|
||||
Self::Warning => write!(f, "warning"),
|
||||
Self::Error => write!(f, "error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum InstEvent {
|
||||
pub enum ServerEvent {
|
||||
Log(LogType, String),
|
||||
NewState(InstState),
|
||||
Log(LogType, String)
|
||||
ConfirmRequest,
|
||||
}
|
||||
|
||||
|
||||
pub enum ClientEvent {
|
||||
ConfirmResponse(bool),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StatusServer {
|
||||
status: Arc<Mutex<InstStatus>>,
|
||||
event_tx: mpsc::Sender<InstEvent>
|
||||
event_tx: mpsc::Sender<ServerEvent>,
|
||||
event_rx: mpsc::Receiver<ClientEvent>,
|
||||
}
|
||||
|
||||
impl StatusServer {
|
||||
@ -118,7 +128,7 @@ impl StatusServer {
|
||||
|
||||
pub fn set_state(&self, new_state: InstState) {
|
||||
self.status.lock().unwrap().state = new_state;
|
||||
self.event_tx.send(InstEvent::NewState(new_state)).unwrap();
|
||||
self.event_tx.send(ServerEvent::NewState(new_state)).unwrap();
|
||||
}
|
||||
|
||||
pub fn set_total(&self, total: usize) {
|
||||
@ -146,8 +156,18 @@ impl StatusServer {
|
||||
self.set_state(InstState::Ignore);
|
||||
}
|
||||
|
||||
pub fn log<S: AsRef<str>>(&self, lt: LogType, msg: S) {
|
||||
self.event_tx.send(InstEvent::Log(lt, msg.as_ref().to_string()))
|
||||
pub fn get_confirmation(&self) -> bool {
|
||||
self.event_tx.send(ServerEvent::ConfirmRequest).unwrap();
|
||||
while let Ok(event) = self.event_rx.recv() {
|
||||
match event {
|
||||
ClientEvent::ConfirmResponse(res) => return res
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn log<S: AsRef<str>>(&self, lt: LogType, msg: S) {
|
||||
self.event_tx.send(ServerEvent::Log(lt, msg.as_ref().to_string()))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -155,6 +175,10 @@ impl StatusServer {
|
||||
self.log(LogType::Info, msg);
|
||||
}
|
||||
|
||||
pub fn log_warning<S: AsRef<str>>(&self, msg: S) {
|
||||
self.log(LogType::Warning, msg);
|
||||
}
|
||||
|
||||
pub fn log_error<S: AsRef<str>>(&self, msg: S) {
|
||||
self.log(LogType::Error, msg);
|
||||
}
|
||||
@ -162,14 +186,44 @@ impl StatusServer {
|
||||
|
||||
|
||||
pub struct StatusClient {
|
||||
pub event_rx: mpsc::Receiver<InstEvent>,
|
||||
status: Arc<Mutex<InstStatus>>
|
||||
status: Arc<Mutex<InstStatus>>,
|
||||
event_tx: mpsc::Sender<ClientEvent>,
|
||||
event_rx: mpsc::Receiver<ServerEvent>,
|
||||
}
|
||||
|
||||
impl StatusClient {
|
||||
pub fn get(&self) -> InstStatus {
|
||||
pub fn get_status(&self) -> InstStatus {
|
||||
self.status.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn receiver(&self) -> &mpsc::Receiver<ServerEvent> {
|
||||
&self.event_rx
|
||||
}
|
||||
|
||||
pub fn confirm(&self, choice: bool) {
|
||||
self.event_tx.send(ClientEvent::ConfirmResponse(choice)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn status_link() -> (StatusServer, StatusClient) {
|
||||
let status1 = Arc::new(Mutex::new(InstStatus::new()));
|
||||
let status2 = status1.clone();
|
||||
let (s_event_tx, s_event_rx) = mpsc::channel();
|
||||
let (c_event_tx, c_event_rx) = mpsc::channel();
|
||||
(
|
||||
StatusServer {
|
||||
status: status1,
|
||||
event_tx: s_event_tx,
|
||||
event_rx: c_event_rx,
|
||||
},
|
||||
StatusClient {
|
||||
status: status2,
|
||||
event_tx: c_event_tx,
|
||||
event_rx: s_event_rx,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -181,17 +235,6 @@ pub struct InstBundle<'a> {
|
||||
}
|
||||
|
||||
|
||||
fn status_channel() -> (StatusServer, StatusClient) {
|
||||
let status1 = Arc::new(Mutex::new(InstStatus::new()));
|
||||
let status2 = status1.clone();
|
||||
let (event_tx, event_rx) = mpsc::channel();
|
||||
(
|
||||
StatusServer {status: status1, event_tx},
|
||||
StatusClient {status: status2, event_rx}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn verify_args(args: &InstArgs) -> anyhow::Result<()> {
|
||||
// TODO: Complete verifications.
|
||||
|
||||
@ -280,14 +323,17 @@ fn open_map(path: PathBuf, flags: sqlite::OpenFlags)
|
||||
}
|
||||
|
||||
|
||||
fn compute_thread(args: InstArgs, status: StatusServer)
|
||||
-> anyhow::Result<()>
|
||||
{
|
||||
fn compute_thread(args: InstArgs, status: StatusServer) -> anyhow::Result<()> {
|
||||
verify_args(&args)?;
|
||||
|
||||
let commands = commands::get_commands();
|
||||
let mut cmd_warning = None;
|
||||
if let Some(cmd_verify) = commands[args.command.as_str()].verify_args {
|
||||
cmd_verify(&args)?
|
||||
cmd_warning = match cmd_verify(&args) {
|
||||
ArgResult::Ok => None,
|
||||
ArgResult::Warning(w) => Some(w),
|
||||
ArgResult::Error(e) => anyhow::bail!(e)
|
||||
}
|
||||
}
|
||||
|
||||
let db_conn = open_map(PathBuf::from(&args.map_path),
|
||||
@ -303,14 +349,27 @@ fn compute_thread(args: InstArgs, status: StatusServer)
|
||||
Some(conn) => Some(MapDatabase::new(conn)?),
|
||||
None => None
|
||||
};
|
||||
// TODO: Standard warning?
|
||||
|
||||
let func = commands[args.command.as_str()].func;
|
||||
let mut inst = InstBundle {args, status, db, idb};
|
||||
func(&mut inst);
|
||||
|
||||
// Issue warnings and confirmation prompt.
|
||||
if inst.args.do_confirmation {
|
||||
inst.status.log_warning(
|
||||
"This tool can permanently damage your Minetest world.\n\
|
||||
Always EXIT Minetest and BACK UP the map database before use.");
|
||||
}
|
||||
if let Some(w) = cmd_warning {
|
||||
inst.status.log_warning(w);
|
||||
}
|
||||
if inst.args.do_confirmation && !inst.status.get_confirmation() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
func(&mut inst); // The real thing!
|
||||
|
||||
let fails = inst.status.get_status().blocks_failed;
|
||||
if fails > 0 {
|
||||
// TODO: log_warning
|
||||
inst.status.log_info(format!(
|
||||
"Skipped {} invalid/unsupported mapblocks.", fails));
|
||||
}
|
||||
@ -327,16 +386,18 @@ fn compute_thread(args: InstArgs, status: StatusServer)
|
||||
pub fn spawn_compute_thread(args: InstArgs)
|
||||
-> (std::thread::JoinHandle<()>, StatusClient)
|
||||
{
|
||||
let (status_tx, status_rx) = status_channel();
|
||||
let (status_server, status_client) = status_link();
|
||||
// Clone within this thread to avoid issue #39364 (hopefully).
|
||||
let status_tx_2 = status_tx.clone();
|
||||
let raw_event_tx = status_server.event_tx.clone();
|
||||
let h = std::thread::Builder::new()
|
||||
.name("compute".to_string())
|
||||
.spawn(move || {
|
||||
compute_thread(args, status_tx_2).unwrap_or_else(
|
||||
|err| status_tx.log_error(&err.to_string())
|
||||
compute_thread(args, status_server).unwrap_or_else(
|
||||
// TODO: Find a cleaner way to do this.
|
||||
|err| raw_event_tx.send(
|
||||
ServerEvent::Log(LogType::Error, err.to_string())).unwrap()
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
(h, status_rx)
|
||||
(h, status_client)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user