Fixed chunk range for restore

+ A single chunk can now also be restored
+ clippy+fmt
backup_error_fix
Joey Hines 2020-11-05 20:35:34 -06:00
parent 5a403b4b64
commit a7e2b260bc
5 changed files with 138 additions and 35 deletions

View File

@ -24,6 +24,9 @@ SUBCOMMANDS:
export Export a backup as a single player world export Export a backup as a single player world
help Prints this message or the help of the given subcommand(s) help Prints this message or the help of the given subcommand(s)
restore Restore certain chunks from a backup restore Restore certain chunks from a backup
Process finished with exit code 1
``` ```
## Examples ## Examples
@ -35,9 +38,13 @@ Exporting a backup to a single player world:
`albatorss -c test.toml export backups/04-11-20_01.51.27_backup.tar.gz sp.tar.gz` `albatorss -c test.toml export backups/04-11-20_01.51.27_backup.tar.gz sp.tar.gz`
Restoring a single chunk (from -2,-2 to 2,2):
`albatorss -c test.toml restore world backups/04-11-20_01.51.27_backup.tar.gz sp.tar.gz` (0,0)
Restoring a range of chunks (from -2,-2 to 2,2): Restoring a range of chunks (from -2,-2 to 2,2):
`albatorss -c test.toml restore world backups/04-11-20_01.51.27_backup.tar.gz sp.tar.gz` -2 -2 2 2 `albatorss -c test.toml restore world backups/04-11-20_01.51.27_backup.tar.gz sp.tar.gz` (-2,-2) -u (2,2)
## Config ## Config
```toml ```toml

View File

@ -219,7 +219,6 @@ pub fn convert_backup_to_sp(
backup: &PathBuf, backup: &PathBuf,
output: &PathBuf, output: &PathBuf,
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
let extract_path = uncompress_backup(backup)?; let extract_path = uncompress_backup(backup)?;
if let Some(worlds) = &config.world_config { if let Some(worlds) = &config.world_config {

View File

@ -0,0 +1,62 @@
use regex::Regex;
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;
use std::str::FromStr;
/// Chunk error
#[derive(Debug)]
pub enum ChunkCoordinateErr {
/// Error parsing integer
ParseIntError(ParseIntError),
/// Regex error
RegexError(regex::Error),
/// Invalid chunk coordinate given
InvalidChunk,
}
impl From<ParseIntError> for ChunkCoordinateErr {
fn from(e: ParseIntError) -> Self {
Self::ParseIntError(e)
}
}
impl From<regex::Error> for ChunkCoordinateErr {
fn from(e: regex::Error) -> Self {
Self::RegexError(e)
}
}
impl fmt::Display for ChunkCoordinateErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unable to parse chunk range: {:?}", self)
}
}
impl Error for ChunkCoordinateErr {}
/// Chunk Coordinate paiir
#[derive(Debug)]
pub struct ChunkCoordinate {
/// X Coordinate
pub x: i32,
/// Z Coordinate
pub z: i32,
}
impl FromStr for ChunkCoordinate {
type Err = ChunkCoordinateErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let re = Regex::new(r"\((?P<x>-?[0-9]*),(?P<z>-?[0-9]*)\)").unwrap();
if let Some(cap) = re.captures(s) {
let x = cap["x"].parse::<i32>()?;
let z = cap["z"].parse::<i32>()?;
Ok(Self { x, z })
} else {
Err(ChunkCoordinateErr::InvalidChunk)
}
}
}

View File

@ -2,14 +2,16 @@ use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
mod backup; mod backup;
mod chunk_coordinate;
mod config; mod config;
mod discord; mod discord;
mod region; mod region;
mod restore; mod restore;
use crate::backup::{convert_backup_to_sp, do_backup}; use crate::backup::{convert_backup_to_sp, do_backup};
use crate::chunk_coordinate::ChunkCoordinate;
use crate::config::AlbatrossConfig; use crate::config::AlbatrossConfig;
use crate::restore::{restore_range_from_backup, restore_chunk_from_backup}; use crate::restore::{restore_chunk_from_backup, restore_range_from_backup};
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
#[structopt(about = "Backup your Minecraft Server!")] #[structopt(about = "Backup your Minecraft Server!")]
@ -50,8 +52,11 @@ enum SubCommand {
/// Backup to restore from /// Backup to restore from
#[structopt(parse(from_os_str))] #[structopt(parse(from_os_str))]
backup_path: PathBuf, backup_path: PathBuf,
/// Backup range can be a single chunk coordinate pair or a chunk range /// Chunk to backup
chunk_range: Vec<i32>, chunk: ChunkCoordinate,
/// Upper chunk range bound
#[structopt(short, long)]
upper_bound: Option<ChunkCoordinate>,
}, },
} }
@ -84,29 +89,34 @@ fn main() {
server_directory, server_directory,
world_name, world_name,
backup_path, backup_path,
chunk_range chunk,
upper_bound,
} => { } => {
println!("Starting restore"); println!("Starting restore");
let server_directory = match server_directory { let server_directory = match server_directory {
Some(dir) => dir, Some(dir) => dir,
None => cfg.backup.minecraft_dir None => cfg.backup.minecraft_dir,
}; };
if chunk_range.len() > 2 { if let Some(upper_bound) = upper_bound {
let lower_x = chunk_range[0]; match restore_range_from_backup(
let lower_z = chunk_range[1]; world_name.as_str(),
let upper_x = chunk_range[2]; chunk,
let upper_z = chunk_range[3]; upper_bound,
match restore_range_from_backup(world_name.as_str(), lower_x, upper_x, lower_z, upper_z, &backup_path, &server_directory) { &backup_path,
&server_directory,
) {
Ok(count) => println!("Restored {} chunks!", count), Ok(count) => println!("Restored {} chunks!", count),
Err(e) => println!("Error restoring backup: {:?}", e), Err(e) => println!("Error restoring backup: {:?}", e),
}; };
} } else {
else if chunk_range.len() == 2 { match restore_chunk_from_backup(
let x = chunk_range[0]; world_name.as_str(),
let z = chunk_range[1]; chunk,
match restore_chunk_from_backup(world_name.as_str(), x, z, &backup_path, &server_directory) { &backup_path,
&server_directory,
) {
Ok(_) => println!("Restored chunk!"), Ok(_) => println!("Restored chunk!"),
Err(e) => println!("Error restoring backup: {:?}", e), Err(e) => println!("Error restoring backup: {:?}", e),
}; };

View File

@ -1,45 +1,64 @@
use anvil_region::AnvilChunkProvider;
use std::path::PathBuf;
use crate::backup::uncompress_backup; use crate::backup::uncompress_backup;
use crate::chunk_coordinate::ChunkCoordinate;
use anvil_region::AnvilChunkProvider;
use std::error; use std::error;
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
use std::path::PathBuf;
struct ChunkAccess { /// Struct for manipulating a world from a backup
struct RestoreAccess {
/// Chunk source
src_path: PathBuf, src_path: PathBuf,
/// Chunk destination
dest_path: PathBuf, dest_path: PathBuf,
} }
impl ChunkAccess { impl RestoreAccess {
pub fn new(world_name: &str, src_path: &PathBuf, dest_path: &PathBuf) -> Result<Self, std::io::Error> { /// Create new RestoreAccess
pub fn new(
world_name: &str,
src_path: &PathBuf,
dest_path: &PathBuf,
) -> Result<Self, std::io::Error> {
let src_path = uncompress_backup(src_path)?.join(world_name).join("region"); let src_path = uncompress_backup(src_path)?.join(world_name).join("region");
let dest_path= dest_path.join("region"); let dest_path = dest_path.join(world_name).join("region");
Ok(ChunkAccess { Ok(RestoreAccess {
src_path, src_path,
dest_path, dest_path,
}) })
} }
pub fn copy_chunk(&self, x:i32, z:i32) { /// Copy chunk from source to desination
pub fn copy_chunk(&self, x: i32, z: i32) {
let src_provider = AnvilChunkProvider::new(self.src_path.to_str().unwrap()); let src_provider = AnvilChunkProvider::new(self.src_path.to_str().unwrap());
let dest_provider = AnvilChunkProvider::new(self.dest_path.to_str().unwrap()); let dest_provider = AnvilChunkProvider::new(self.dest_path.to_str().unwrap());
let chunk = src_provider.load_chunk(x, z).expect("Unable to load chunk"); let chunk = src_provider.load_chunk(x, z).expect("Unable to load chunk");
dest_provider.save_chunk(x, z, chunk).expect("Unable to save chunk"); dest_provider
.save_chunk(x, z, chunk)
.expect("Unable to save chunk");
} }
/// Cleanup process
pub fn cleanup(self) -> Result<(), std::io::Error> { pub fn cleanup(self) -> Result<(), std::io::Error> {
remove_dir_all("tmp") remove_dir_all("tmp")
} }
} }
pub fn restore_range_from_backup(world_name: &str, lower_x: i32, upper_x: i32, lower_z: i32, upper_z: i32, backup_path: &PathBuf, minecraft_dir: &PathBuf) -> Result<u64, Box<dyn error::Error>> { /// Restore a range of chunks from a backup
let chunk_access = ChunkAccess::new(world_name, backup_path, minecraft_dir)?; pub fn restore_range_from_backup(
world_name: &str,
lower: ChunkCoordinate,
upper: ChunkCoordinate,
backup_path: &PathBuf,
minecraft_dir: &PathBuf,
) -> Result<u64, Box<dyn error::Error>> {
let chunk_access = RestoreAccess::new(world_name, backup_path, minecraft_dir)?;
let mut count = 0; let mut count = 0;
for x in lower_x..upper_x { for x in lower.x..=upper.x {
for z in lower_z..upper_z { for z in lower.z..=upper.z {
chunk_access.copy_chunk(x, z); chunk_access.copy_chunk(x, z);
count += 1; count += 1;
} }
@ -49,9 +68,15 @@ pub fn restore_range_from_backup(world_name: &str, lower_x: i32, upper_x: i32, l
Ok(count) Ok(count)
} }
pub fn restore_chunk_from_backup(world_name: &str, x: i32, z: i32, backup_path: &PathBuf, minecraft_dir: &PathBuf) -> Result<(), Box<dyn error::Error>> { /// Restore a single chunk from a backup
let chunk_access = ChunkAccess::new(world_name, backup_path, minecraft_dir)?; pub fn restore_chunk_from_backup(
chunk_access.copy_chunk(x, z); world_name: &str,
chunk: ChunkCoordinate,
backup_path: &PathBuf,
minecraft_dir: &PathBuf,
) -> Result<(), Box<dyn error::Error>> {
let chunk_access = RestoreAccess::new(world_name, backup_path, minecraft_dir)?;
chunk_access.copy_chunk(chunk.x, chunk.z);
chunk_access.cleanup()?; chunk_access.cleanup()?;
Ok(()) Ok(())