Fixed chunk range for restore
+ A single chunk can now also be restored + clippy+fmtbackup_error_fix
parent
5a403b4b64
commit
a7e2b260bc
|
@ -24,6 +24,9 @@ SUBCOMMANDS:
|
|||
export Export a backup as a single player world
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
restore Restore certain chunks from a backup
|
||||
|
||||
Process finished with exit code 1
|
||||
|
||||
```
|
||||
|
||||
## 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`
|
||||
|
||||
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):
|
||||
|
||||
`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
|
||||
```toml
|
||||
|
|
|
@ -219,7 +219,6 @@ pub fn convert_backup_to_sp(
|
|||
backup: &PathBuf,
|
||||
output: &PathBuf,
|
||||
) -> Result<(), std::io::Error> {
|
||||
|
||||
let extract_path = uncompress_backup(backup)?;
|
||||
|
||||
if let Some(worlds) = &config.world_config {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
42
src/main.rs
42
src/main.rs
|
@ -2,14 +2,16 @@ use std::path::PathBuf;
|
|||
use structopt::StructOpt;
|
||||
|
||||
mod backup;
|
||||
mod chunk_coordinate;
|
||||
mod config;
|
||||
mod discord;
|
||||
mod region;
|
||||
mod restore;
|
||||
|
||||
use crate::backup::{convert_backup_to_sp, do_backup};
|
||||
use crate::chunk_coordinate::ChunkCoordinate;
|
||||
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)]
|
||||
#[structopt(about = "Backup your Minecraft Server!")]
|
||||
|
@ -50,8 +52,11 @@ enum SubCommand {
|
|||
/// Backup to restore from
|
||||
#[structopt(parse(from_os_str))]
|
||||
backup_path: PathBuf,
|
||||
/// Backup range can be a single chunk coordinate pair or a chunk range
|
||||
chunk_range: Vec<i32>,
|
||||
/// Chunk to backup
|
||||
chunk: ChunkCoordinate,
|
||||
/// Upper chunk range bound
|
||||
#[structopt(short, long)]
|
||||
upper_bound: Option<ChunkCoordinate>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -84,29 +89,34 @@ fn main() {
|
|||
server_directory,
|
||||
world_name,
|
||||
backup_path,
|
||||
chunk_range
|
||||
chunk,
|
||||
upper_bound,
|
||||
} => {
|
||||
println!("Starting restore");
|
||||
|
||||
let server_directory = match server_directory {
|
||||
Some(dir) => dir,
|
||||
None => cfg.backup.minecraft_dir
|
||||
None => cfg.backup.minecraft_dir,
|
||||
};
|
||||
|
||||
if chunk_range.len() > 2 {
|
||||
let lower_x = chunk_range[0];
|
||||
let lower_z = chunk_range[1];
|
||||
let upper_x = chunk_range[2];
|
||||
let upper_z = chunk_range[3];
|
||||
match restore_range_from_backup(world_name.as_str(), lower_x, upper_x, lower_z, upper_z, &backup_path, &server_directory) {
|
||||
if let Some(upper_bound) = upper_bound {
|
||||
match restore_range_from_backup(
|
||||
world_name.as_str(),
|
||||
chunk,
|
||||
upper_bound,
|
||||
&backup_path,
|
||||
&server_directory,
|
||||
) {
|
||||
Ok(count) => println!("Restored {} chunks!", count),
|
||||
Err(e) => println!("Error restoring backup: {:?}", e),
|
||||
};
|
||||
}
|
||||
else if chunk_range.len() == 2 {
|
||||
let x = chunk_range[0];
|
||||
let z = chunk_range[1];
|
||||
match restore_chunk_from_backup(world_name.as_str(), x, z, &backup_path, &server_directory) {
|
||||
} else {
|
||||
match restore_chunk_from_backup(
|
||||
world_name.as_str(),
|
||||
chunk,
|
||||
&backup_path,
|
||||
&server_directory,
|
||||
) {
|
||||
Ok(_) => println!("Restored chunk!"),
|
||||
Err(e) => println!("Error restoring backup: {:?}", e),
|
||||
};
|
||||
|
|
|
@ -1,45 +1,64 @@
|
|||
use anvil_region::AnvilChunkProvider;
|
||||
use std::path::PathBuf;
|
||||
use crate::backup::uncompress_backup;
|
||||
use crate::chunk_coordinate::ChunkCoordinate;
|
||||
use anvil_region::AnvilChunkProvider;
|
||||
use std::error;
|
||||
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,
|
||||
/// Chunk destination
|
||||
dest_path: PathBuf,
|
||||
}
|
||||
|
||||
impl ChunkAccess {
|
||||
pub fn new(world_name: &str, src_path: &PathBuf, dest_path: &PathBuf) -> Result<Self, std::io::Error> {
|
||||
impl RestoreAccess {
|
||||
/// 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 dest_path= dest_path.join("region");
|
||||
let dest_path = dest_path.join(world_name).join("region");
|
||||
|
||||
Ok(ChunkAccess {
|
||||
Ok(RestoreAccess {
|
||||
src_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 dest_provider = AnvilChunkProvider::new(self.dest_path.to_str().unwrap());
|
||||
|
||||
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> {
|
||||
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>> {
|
||||
let chunk_access = ChunkAccess::new(world_name, backup_path, minecraft_dir)?;
|
||||
/// Restore a range of chunks from a backup
|
||||
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;
|
||||
|
||||
for x in lower_x..upper_x {
|
||||
for z in lower_z..upper_z {
|
||||
for x in lower.x..=upper.x {
|
||||
for z in lower.z..=upper.z {
|
||||
chunk_access.copy_chunk(x, z);
|
||||
count += 1;
|
||||
}
|
||||
|
@ -49,9 +68,15 @@ pub fn restore_range_from_backup(world_name: &str, lower_x: i32, upper_x: i32, l
|
|||
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>> {
|
||||
let chunk_access = ChunkAccess::new(world_name, backup_path, minecraft_dir)?;
|
||||
chunk_access.copy_chunk(x, z);
|
||||
/// Restore a single chunk from a backup
|
||||
pub fn restore_chunk_from_backup(
|
||||
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()?;
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue