mirror of
https://git.ahines.net/joeyahines/Albatross.git
synced 2026-11-08 09:13:57 +00:00
Added the ability to restore chunks from a backup
+ Added `restore` subcommand + Needs more testing, but appears to be working
This commit is contained in:
1 parent
b9aad433ba
commit
a94d0221c5
5 files changed
+176
-11
No files matched your search
+12
-5
@@ -198,6 +198,16 @@ pub fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), s
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uncompress_backup(backup: &PathBuf) -> Result<PathBuf, std::io::Error> {
|
||||
let backup_file = File::open(backup)?;
|
||||
let dec = GzDecoder::new(backup_file);
|
||||
let mut extract = Archive::new(dec);
|
||||
let extract_path = PathBuf::from("tmp");
|
||||
extract.unpack(&extract_path)?;
|
||||
|
||||
Ok(extract_path)
|
||||
}
|
||||
|
||||
/// Takes an existing backup and converts it to a singleplayer world
|
||||
///
|
||||
/// # Param
|
||||
@@ -209,11 +219,8 @@ pub fn convert_backup_to_sp(
|
||||
backup: &PathBuf,
|
||||
output: &PathBuf,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let backup_file = File::open(backup)?;
|
||||
let dec = GzDecoder::new(backup_file);
|
||||
let mut extract = Archive::new(dec);
|
||||
let extract_path = PathBuf::from("tmp");
|
||||
extract.unpack(&extract_path)?;
|
||||
|
||||
let extract_path = uncompress_backup(backup)?;
|
||||
|
||||
if let Some(worlds) = &config.world_config {
|
||||
for world in worlds {
|
||||
|
||||
+49
-2
@@ -5,9 +5,11 @@ mod backup;
|
||||
mod config;
|
||||
mod discord;
|
||||
mod region;
|
||||
mod restore;
|
||||
|
||||
use crate::backup::{convert_backup_to_sp, do_backup};
|
||||
use crate::config::AlbatrossConfig;
|
||||
use crate::restore::{restore_range_from_backup, restore_chunk_from_backup};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(about = "Backup your Minecraft Server!")]
|
||||
@@ -24,13 +26,13 @@ struct Albatross {
|
||||
enum SubCommand {
|
||||
/// Backup a server
|
||||
Backup {
|
||||
/// Output location override
|
||||
/// Output path
|
||||
#[structopt(short = "o", long = "ouptut", parse(from_os_str))]
|
||||
output: Option<PathBuf>,
|
||||
},
|
||||
/// Export a backup as a single player world
|
||||
Export {
|
||||
/// Convert backup to singleplayer world
|
||||
/// Backup to convert
|
||||
#[structopt(parse(from_os_str))]
|
||||
input_backup: PathBuf,
|
||||
|
||||
@@ -38,6 +40,19 @@ enum SubCommand {
|
||||
#[structopt(parse(from_os_str))]
|
||||
output: PathBuf,
|
||||
},
|
||||
/// Restore certain chunks from a backup
|
||||
Restore {
|
||||
/// Output server directory
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
server_directory: Option<PathBuf>,
|
||||
/// World to restore
|
||||
world_name: String,
|
||||
/// 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>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -65,6 +80,38 @@ fn main() {
|
||||
Err(e) => println!("Error exporting backup: {:?}", e),
|
||||
};
|
||||
}
|
||||
SubCommand::Restore {
|
||||
server_directory,
|
||||
world_name,
|
||||
backup_path,
|
||||
chunk_range
|
||||
} => {
|
||||
println!("Starting restore");
|
||||
|
||||
let server_directory = match server_directory {
|
||||
Some(dir) => 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) {
|
||||
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) {
|
||||
Ok(_) => println!("Restored chunk!"),
|
||||
Err(e) => println!("Error restoring backup: {:?}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("No worlds specified in config file!")
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
use anvil_region::AnvilChunkProvider;
|
||||
use std::path::PathBuf;
|
||||
use crate::backup::uncompress_backup;
|
||||
use std::error;
|
||||
use std::fs::remove_dir_all;
|
||||
|
||||
struct ChunkAccess {
|
||||
src_path: PathBuf,
|
||||
dest_path: PathBuf,
|
||||
}
|
||||
|
||||
impl ChunkAccess {
|
||||
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");
|
||||
|
||||
Ok(ChunkAccess {
|
||||
src_path,
|
||||
dest_path,
|
||||
})
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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)?;
|
||||
let mut count = 0;
|
||||
|
||||
for x in lower_x..upper_x {
|
||||
for z in lower_z..upper_z {
|
||||
chunk_access.copy_chunk(x, z);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
chunk_access.cleanup()?;
|
||||
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);
|
||||
|
||||
chunk_access.cleanup()?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user