Added 26.1 world format
This commit is contained in:
parent
38630a4d57
commit
a22c781c52
661
Cargo.lock
generated
661
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "albatross"
|
name = "albatross"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
authors = ["Joey Hines <joey@ahines.net>"]
|
authors = ["Joey Hines <joey@ahines.net>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ chrono = "0.4"
|
|||||||
regex = "1.3.9"
|
regex = "1.3.9"
|
||||||
flate2 = "1.0.14"
|
flate2 = "1.0.14"
|
||||||
tar = "0.4.28"
|
tar = "0.4.28"
|
||||||
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
|
reqwest = { version = "0.13.2", features = ["blocking", "json"] }
|
||||||
discord-hooks-rs = { git = "https://github.com/joeyahines/discord-hooks-rs" }
|
discord-hooks-rs = { git = "https://github.com/joeyahines/discord-hooks-rs" }
|
||||||
anvil-region = "0.8.1"
|
anvil-region = "0.8.1"
|
||||||
ssh2 = "0.9.1"
|
ssh2 = "0.9.1"
|
||||||
|
|||||||
45
README.md
45
README.md
@ -8,7 +8,7 @@ Backups can also be transferred to a remote server using SFTP.
|
|||||||
|
|
||||||
## Help
|
## Help
|
||||||
```
|
```
|
||||||
albatross 0.4.0
|
albatross 0.7.0
|
||||||
Backup your Minecraft Server!
|
Backup your Minecraft Server!
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
@ -25,10 +25,7 @@ SUBCOMMANDS:
|
|||||||
backup Backup a server
|
backup Backup a server
|
||||||
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
|
||||||
@ -49,6 +46,44 @@ 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) -u (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
|
||||||
|
|
||||||
|
### For Minecraft Versions After 26.1
|
||||||
|
```toml
|
||||||
|
[backup]
|
||||||
|
# Minecraft sever directory
|
||||||
|
minecraft_dir = "/home/mc/server"
|
||||||
|
# Optional Discord webhook
|
||||||
|
discord_webhook = "https://discordapp.com/api/webhooks/"
|
||||||
|
# Number of backups to keep
|
||||||
|
backups_to_keep = 10
|
||||||
|
|
||||||
|
[backup.output_config]
|
||||||
|
# Directory to place backups
|
||||||
|
path = "./backups"
|
||||||
|
|
||||||
|
[world_26_plus_config]
|
||||||
|
world_name = "world"
|
||||||
|
|
||||||
|
[[world_26_plus_config.dimensions]]
|
||||||
|
# world name
|
||||||
|
world_name = "minecraft/overworld"
|
||||||
|
# world save radius (in blocks)
|
||||||
|
save_radius = 1000
|
||||||
|
|
||||||
|
[[world_26_plus_config.dimensions]]
|
||||||
|
# world name
|
||||||
|
world_name = "minecraft/the_end"
|
||||||
|
# world save radius (in blocks)
|
||||||
|
save_radius = 1000
|
||||||
|
|
||||||
|
[[world_26_plus_config.dimensions]]
|
||||||
|
# world name
|
||||||
|
world_name = "minecraft/the_nether"
|
||||||
|
# world save radius (in blocks)
|
||||||
|
save_radius = 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Minecraft Versions Before 26.1
|
||||||
```toml
|
```toml
|
||||||
# Local Backup Config
|
# Local Backup Config
|
||||||
[backup]
|
[backup]
|
||||||
|
|||||||
152
src/backup.rs
152
src/backup.rs
@ -1,6 +1,8 @@
|
|||||||
use crate::config::{AlbatrossConfig, RemoteBackupConfig, WorldConfig, WorldType};
|
use crate::config::{
|
||||||
|
AlbatrossConfig, RemoteBackupConfig, World26PlusConfig, WorldConfig, WorldType,
|
||||||
|
};
|
||||||
use crate::discord::send_webhook;
|
use crate::discord::send_webhook;
|
||||||
use crate::error::Result;
|
use crate::error::{AlbatrossError, Result};
|
||||||
use crate::region::Region;
|
use crate::region::Region;
|
||||||
use crate::remote::RemoteBackupSite;
|
use crate::remote::RemoteBackupSite;
|
||||||
use crate::remote::file::FileBackup;
|
use crate::remote::file::FileBackup;
|
||||||
@ -43,22 +45,35 @@ pub fn backup_file(file_name: &str, world_path: &Path, backup_path: &Path) -> Re
|
|||||||
pub fn backup_dir(dir_name: &str, world_path: &Path, backup_path: &Path) -> Result<u64> {
|
pub fn backup_dir(dir_name: &str, world_path: &Path, backup_path: &Path) -> Result<u64> {
|
||||||
let src_dir = world_path.join(dir_name);
|
let src_dir = world_path.join(dir_name);
|
||||||
|
|
||||||
if !src_dir.exists() {
|
if !src_dir.exists() || !src_dir.is_dir() {
|
||||||
warn!("Directory '{dir_name}' does not exist in '{world_path:?}'");
|
warn!("Directory '{dir_name}' does not exist in '{world_path:?}'");
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let backup_dir = backup_path.join(dir_name);
|
let backup_dir_path = backup_path.join(dir_name);
|
||||||
create_dir(&backup_dir)?;
|
create_dir(&backup_dir_path)?;
|
||||||
|
|
||||||
let mut file_count = 0;
|
let mut file_count = 0;
|
||||||
for entry in src_dir.read_dir()? {
|
for entry in src_dir.read_dir()? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let mut target = backup_dir.clone();
|
let mut target = backup_dir_path.clone();
|
||||||
target.push(entry.file_name());
|
|
||||||
|
|
||||||
copy(entry.path(), target)?;
|
if entry.path().is_dir() {
|
||||||
file_count += 1;
|
let sub_dir = entry.file_name();
|
||||||
|
let world_path = world_path.join(dir_name);
|
||||||
|
let backup_path = backup_path.join(dir_name);
|
||||||
|
|
||||||
|
if !backup_path.exists() {
|
||||||
|
create_dir(&backup_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_count += backup_dir(sub_dir.to_str().unwrap(), &world_path, &backup_path)?;
|
||||||
|
} else {
|
||||||
|
target.push(entry.file_name());
|
||||||
|
|
||||||
|
copy(entry.path(), target)?;
|
||||||
|
file_count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(file_count)
|
Ok(file_count)
|
||||||
@ -94,14 +109,15 @@ pub fn backup_region(
|
|||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let file_name = entry.file_name().to_str().unwrap().to_string();
|
let file_name = entry.file_name().to_str().unwrap().to_string();
|
||||||
|
|
||||||
if let Ok(region) = Region::try_from(file_name) {
|
if let Ok(region) = Region::try_from(file_name)
|
||||||
if region.x.abs() <= save_radius && region.y.abs() <= save_radius {
|
&& region.x.abs() <= save_radius
|
||||||
let mut target = backup_dir.clone();
|
&& region.y.abs() <= save_radius
|
||||||
target.push(entry.file_name());
|
{
|
||||||
|
let mut target = backup_dir.clone();
|
||||||
|
target.push(entry.file_name());
|
||||||
|
|
||||||
copy(entry.path(), target)?;
|
copy(entry.path(), target)?;
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +286,7 @@ pub fn do_remote_backup(
|
|||||||
/// * `cfg` - config file
|
/// * `cfg` - config file
|
||||||
pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<()> {
|
pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<()> {
|
||||||
let server_base_dir = cfg.backup.minecraft_dir.clone();
|
let server_base_dir = cfg.backup.minecraft_dir.clone();
|
||||||
let worlds = cfg.world_config.clone().expect("No worlds configured");
|
let timer = Instant::now();
|
||||||
let time_str = Utc::now().format("%d-%m-%y_%H.%M.%S").to_string();
|
let time_str = Utc::now().format("%d-%m-%y_%H.%M.%S").to_string();
|
||||||
let backup_name = format!("{time_str}_backup.tar.gz");
|
let backup_name = format!("{time_str}_backup.tar.gz");
|
||||||
let mut output_archive = match output {
|
let mut output_archive = match output {
|
||||||
@ -284,15 +300,21 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<()> {
|
|||||||
|
|
||||||
create_dir_all(tmp_dir.clone())?;
|
create_dir_all(tmp_dir.clone())?;
|
||||||
|
|
||||||
let timer = Instant::now();
|
|
||||||
|
|
||||||
send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg);
|
send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg);
|
||||||
|
|
||||||
backup_worlds(&cfg, server_base_dir, worlds, &tmp_dir).map_err(|e| {
|
let backup_res = if let Some(worlds) = &cfg.world_config {
|
||||||
send_webhook("Failed to copy worlds to backup location", &cfg);
|
backup_worlds(&cfg, server_base_dir, worlds, &tmp_dir)
|
||||||
error!("Failed to copy worlds: {e}");
|
} else if let Some(world_config) = &cfg.world_26_plus_config {
|
||||||
e
|
backup_v26_plus_world_format(&cfg, world_config, &tmp_dir)
|
||||||
})?;
|
} else {
|
||||||
|
Err(AlbatrossError::MissingConfig)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = backup_res {
|
||||||
|
send_webhook("Failed to backup worlds", &cfg);
|
||||||
|
error!("Failed to backup worlds: {err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
compress_backup(&tmp_dir, &output_archive).map_err(|e| {
|
compress_backup(&tmp_dir, &output_archive).map_err(|e| {
|
||||||
send_webhook("Failed to compress backup", &cfg);
|
send_webhook("Failed to compress backup", &cfg);
|
||||||
@ -302,8 +324,7 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<()> {
|
|||||||
|
|
||||||
remove_dir_all(&tmp_dir)?;
|
remove_dir_all(&tmp_dir)?;
|
||||||
|
|
||||||
let mut local_backup =
|
let mut local_backup = FileBackup::new(&cfg.backup.output_config, cfg.backup.backups_to_keep)?;
|
||||||
FileBackup::new(&cfg.backup.output_config, cfg.backup.backups_to_keep).unwrap();
|
|
||||||
|
|
||||||
match local_backup.cleanup() {
|
match local_backup.cleanup() {
|
||||||
Ok(backups_removed) => {
|
Ok(backups_removed) => {
|
||||||
@ -342,10 +363,81 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Backup a dimension
|
||||||
|
///
|
||||||
|
/// # Param
|
||||||
|
/// * `world_path` - path to the world folder
|
||||||
|
/// * `backup_path` - path to the backup folder
|
||||||
|
/// * `world_config` - world config options
|
||||||
|
pub fn backup_dimension(
|
||||||
|
world_path: &Path,
|
||||||
|
backup_path: &Path,
|
||||||
|
world_config: &WorldConfig,
|
||||||
|
) -> Result<u64> {
|
||||||
|
let dimension_path = Path::new("dimensions").join(&world_config.world_name);
|
||||||
|
let backup_path = backup_path.join(&dimension_path);
|
||||||
|
let src_path = world_path.join(dimension_path);
|
||||||
|
create_dir_all(backup_path.as_path())?;
|
||||||
|
|
||||||
|
backup_dir("data", &src_path, &backup_path)?;
|
||||||
|
let region_count = backup_region("region", world_config.save_radius, &src_path, &backup_path)?;
|
||||||
|
Ok(region_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backup v26.1 + version
|
||||||
|
///
|
||||||
|
/// # Param
|
||||||
|
/// * `world_path` - path to the world folder
|
||||||
|
/// * `backup_path` - path to the backup folder
|
||||||
|
/// * `world_config` - world config options
|
||||||
|
pub fn backup_v26_plus_world_format(
|
||||||
|
cfg: &AlbatrossConfig,
|
||||||
|
world_config: &World26PlusConfig,
|
||||||
|
tmp_dir: &Path,
|
||||||
|
) -> Result<()> {
|
||||||
|
let world_path = cfg.backup.minecraft_dir.join(&world_config.world_name);
|
||||||
|
let backup_path = tmp_dir.join(&world_config.world_name);
|
||||||
|
|
||||||
|
if !backup_path.exists() {
|
||||||
|
create_dir(&backup_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_webhook(
|
||||||
|
format!("Starting backup **{}**", world_config.world_name).as_str(),
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Backup common files
|
||||||
|
backup_dir("data", &world_path, &backup_path)?;
|
||||||
|
backup_dir("datapacks", &world_path, &backup_path)?;
|
||||||
|
backup_file("level.dat", &world_path, &backup_path)?;
|
||||||
|
backup_file("level.dat_old", &world_path, &backup_path).ok();
|
||||||
|
backup_file("session.lock", &world_path, &backup_path).ok();
|
||||||
|
let player_count = backup_dir("players", &world_path, &backup_path)?;
|
||||||
|
send_webhook(format!("Backed up {player_count} players").as_str(), cfg);
|
||||||
|
info!("Backed up {player_count} players");
|
||||||
|
|
||||||
|
for world_config in &world_config.dimensions {
|
||||||
|
send_webhook(
|
||||||
|
format!("Starting backup of **{}**", world_config.world_name).as_str(),
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"Starting backup of dimension **{}**",
|
||||||
|
world_config.world_name
|
||||||
|
);
|
||||||
|
let region_count = backup_dimension(&world_path, &backup_path, world_config)?;
|
||||||
|
send_webhook(format!("{region_count} regions backed up.").as_str(), cfg);
|
||||||
|
info!("{region_count} regions backed up.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn backup_worlds(
|
fn backup_worlds(
|
||||||
cfg: &AlbatrossConfig,
|
cfg: &AlbatrossConfig,
|
||||||
server_base_dir: PathBuf,
|
server_base_dir: PathBuf,
|
||||||
worlds: Vec<WorldConfig>,
|
worlds: &[WorldConfig],
|
||||||
tmp_dir: &Path,
|
tmp_dir: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for world in worlds {
|
for world in worlds {
|
||||||
@ -360,15 +452,15 @@ fn backup_worlds(
|
|||||||
let webhook_msg = match world_type {
|
let webhook_msg = match world_type {
|
||||||
WorldType::Overworld => {
|
WorldType::Overworld => {
|
||||||
let (region_count, player_count) =
|
let (region_count, player_count) =
|
||||||
backup_overworld(&world_dir.clone(), tmp_dir, &world)?;
|
backup_overworld(&world_dir.clone(), tmp_dir, world)?;
|
||||||
format!("{region_count} regions and {player_count} player files backed up.")
|
format!("{region_count} regions and {player_count} player files backed up.")
|
||||||
}
|
}
|
||||||
WorldType::Nether => {
|
WorldType::Nether => {
|
||||||
let region_count = backup_nether(&world_dir, tmp_dir, &world)?;
|
let region_count = backup_nether(&world_dir, tmp_dir, world)?;
|
||||||
format!("{region_count} regions backed up.")
|
format!("{region_count} regions backed up.")
|
||||||
}
|
}
|
||||||
WorldType::End => {
|
WorldType::End => {
|
||||||
let region_count = backup_end(&world_dir, tmp_dir, &world)?;
|
let region_count = backup_end(&world_dir, tmp_dir, world)?;
|
||||||
format!("{region_count} regions backed up.")
|
format!("{region_count} regions backed up.")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -64,11 +64,19 @@ pub struct RemoteBackupConfig {
|
|||||||
pub file: Option<FileConfig>,
|
pub file: Option<FileConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Config for individual world configuration
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct World26PlusConfig {
|
||||||
|
pub world_name: String,
|
||||||
|
pub dimensions: Vec<WorldConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Configs
|
/// Configs
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct AlbatrossConfig {
|
pub struct AlbatrossConfig {
|
||||||
pub backup: BackupConfig,
|
pub backup: BackupConfig,
|
||||||
pub world_config: Option<Vec<WorldConfig>>,
|
pub world_config: Option<Vec<WorldConfig>>,
|
||||||
|
pub world_26_plus_config: Option<World26PlusConfig>,
|
||||||
pub remote: Option<RemoteBackupConfig>,
|
pub remote: Option<RemoteBackupConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ pub enum AlbatrossError {
|
|||||||
ChronoParseError(chrono::ParseError),
|
ChronoParseError(chrono::ParseError),
|
||||||
NoSSHAuth,
|
NoSSHAuth,
|
||||||
FTPError(ftp::FtpError),
|
FTPError(ftp::FtpError),
|
||||||
|
MissingConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for AlbatrossError {}
|
impl std::error::Error for AlbatrossError {}
|
||||||
@ -27,6 +28,7 @@ impl std::fmt::Display for AlbatrossError {
|
|||||||
AlbatrossError::ChronoParseError(e) => write!(f, "Unable to parse time: {e}"),
|
AlbatrossError::ChronoParseError(e) => write!(f, "Unable to parse time: {e}"),
|
||||||
AlbatrossError::NoSSHAuth => write!(f, "No SSH auth methods provided in the config"),
|
AlbatrossError::NoSSHAuth => write!(f, "No SSH auth methods provided in the config"),
|
||||||
AlbatrossError::FTPError(e) => write!(f, "FTP error: {e}"),
|
AlbatrossError::FTPError(e) => write!(f, "FTP error: {e}"),
|
||||||
|
AlbatrossError::MissingConfig => write!(f, "Missing world config"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
106
src/main.rs
106
src/main.rs
@ -74,64 +74,60 @@ fn main() {
|
|||||||
let cfg = AlbatrossConfig::new(opt.config_path.into_os_string().to_str().unwrap())
|
let cfg = AlbatrossConfig::new(opt.config_path.into_os_string().to_str().unwrap())
|
||||||
.expect("Config error");
|
.expect("Config error");
|
||||||
|
|
||||||
if cfg.world_config.is_some() {
|
match opt.sub_command {
|
||||||
match opt.sub_command {
|
SubCommand::Backup { output } => {
|
||||||
SubCommand::Backup { output } => {
|
info!("Starting backup");
|
||||||
info!("Starting backup");
|
match do_backup(cfg, output) {
|
||||||
match do_backup(cfg, output) {
|
Ok(_) => info!("Backup complete!"),
|
||||||
Ok(_) => info!("Backup complete!"),
|
Err(e) => info!("Error doing backup: {e:?}"),
|
||||||
Err(e) => info!("Error doing backup: {e:?}"),
|
};
|
||||||
};
|
}
|
||||||
}
|
SubCommand::Export {
|
||||||
SubCommand::Export {
|
input_backup,
|
||||||
input_backup,
|
output,
|
||||||
output,
|
} => {
|
||||||
} => {
|
info!("Starting export");
|
||||||
info!("Starting export");
|
match convert_backup_to_sp(&cfg, &input_backup, &output) {
|
||||||
match convert_backup_to_sp(&cfg, &input_backup, &output) {
|
Ok(_) => info!("Export complete!"),
|
||||||
Ok(_) => info!("Export complete!"),
|
Err(e) => info!("Error exporting backup: {e:?}"),
|
||||||
Err(e) => info!("Error exporting backup: {e:?}"),
|
};
|
||||||
};
|
}
|
||||||
}
|
SubCommand::Restore {
|
||||||
SubCommand::Restore {
|
server_directory,
|
||||||
server_directory,
|
world_name,
|
||||||
world_name,
|
backup_path,
|
||||||
backup_path,
|
chunk,
|
||||||
chunk,
|
upper_bound,
|
||||||
upper_bound,
|
} => {
|
||||||
} => {
|
info!("Starting restore");
|
||||||
info!("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 let Some(upper_bound) = upper_bound {
|
if let Some(upper_bound) = upper_bound {
|
||||||
match restore_range_from_backup(
|
match restore_range_from_backup(
|
||||||
world_name.as_str(),
|
world_name.as_str(),
|
||||||
chunk,
|
chunk,
|
||||||
upper_bound,
|
upper_bound,
|
||||||
&backup_path,
|
&backup_path,
|
||||||
&server_directory,
|
&server_directory,
|
||||||
) {
|
) {
|
||||||
Ok(count) => info!("Restored {count} chunks!"),
|
Ok(count) => info!("Restored {count} chunks!"),
|
||||||
Err(e) => info!("Error restoring backup: {e:?}"),
|
Err(e) => info!("Error restoring backup: {e:?}"),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
match restore_chunk_from_backup(
|
match restore_chunk_from_backup(
|
||||||
world_name.as_str(),
|
world_name.as_str(),
|
||||||
chunk,
|
chunk,
|
||||||
&backup_path,
|
&backup_path,
|
||||||
&server_directory,
|
&server_directory,
|
||||||
) {
|
) {
|
||||||
Ok(_) => info!("Restored chunk!"),
|
Ok(_) => info!("Restored chunk!"),
|
||||||
Err(e) => info!("Error restoring backup: {e:?}"),
|
Err(e) => info!("Error restoring backup: {e:?}"),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
info!("No worlds specified in config file!")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user