parent
9bdba6e962
commit
50d09881ff
|
@ -94,16 +94,16 @@ public class Globals {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!executorService.awaitTermination(timeout, TimeUnit.MICROSECONDS)) {
|
if(!executorService.awaitTermination(timeout, TimeUnit.MICROSECONDS)) {
|
||||||
log.error("Timeout occurred while waiting for currently running backups to finish!");
|
log.error("在等待当前运行的备份完成时发生了超时!");
|
||||||
executorService.shutdownNow().stream()
|
executorService.shutdownNow().stream()
|
||||||
// .filter(r -> r instanceof ExecutableBackup)
|
// .filter(r -> r instanceof ExecutableBackup)
|
||||||
// .map(r -> (ExecutableBackup)r)
|
// .map(r -> (ExecutableBackup)r)
|
||||||
.forEach(r -> log.error("Dropping: {}", r.toString()));
|
.forEach(r -> log.error("Dropping: {}", r.toString()));
|
||||||
if(!executorService.awaitTermination(1000, TimeUnit.MICROSECONDS))
|
if(!executorService.awaitTermination(1000, TimeUnit.MICROSECONDS))
|
||||||
log.error("Couldn't shut down the executor!");
|
log.error("无法关闭执行器!");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
log.error("An exception occurred!", e);
|
log.error("发生了一个异常!(灾难性的错误)", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -124,12 +124,12 @@ public class Globals {
|
||||||
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >=
|
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >=
|
||||||
tmp_dir.toFile().getUsableSpace()
|
tmp_dir.toFile().getUsableSpace()
|
||||||
) {
|
) {
|
||||||
log.error("Not enough space left in TMP directory! ({})", tmp_dir);
|
log.error("TMP(临时)文件目录没有可用空间 ({})", tmp_dir);
|
||||||
disableTMPFiles = true;
|
disableTMPFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Files.isWritable(tmp_dir)) {
|
if(!Files.isWritable(tmp_dir)) {
|
||||||
log.error("TMP filesystem ({}) is read-only!", tmp_dir);
|
log.error("TMP(临时)文件目录({})是只读的", tmp_dir);
|
||||||
disableTMPFiles = true;
|
disableTMPFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class CleanupCommand {
|
||||||
private static int execute(ServerCommandSource source) {
|
private static int execute(ServerCommandSource source) {
|
||||||
log.sendInfo(
|
log.sendInfo(
|
||||||
source,
|
source,
|
||||||
"Deleted: {} files.",
|
"删除了: {} 文件.",
|
||||||
new Cleanup(source, Utilities.getLevelName(source.getServer())).call()
|
new Cleanup(source, Utilities.getLevelName(source.getServer())).call()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class BlacklistCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int help(ServerCommandSource source) {
|
private static int help(ServerCommandSource source) {
|
||||||
log.sendInfo(source, "Available command are: add [player], remove [player], list.");
|
log.sendInfo(source, "可用的命令有:add [player],remove [player],list. ");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class BlacklistCommand {
|
||||||
private static int executeList(ServerCommandSource source) {
|
private static int executeList(ServerCommandSource source) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("Currently on the blacklist are: ");
|
builder.append("目前在黑名单上的有:");
|
||||||
|
|
||||||
for(String name : config.get().playerBlacklist){
|
for(String name : config.get().playerBlacklist){
|
||||||
builder.append(name);
|
builder.append(name);
|
||||||
|
@ -73,24 +73,24 @@ public class BlacklistCommand {
|
||||||
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
||||||
|
|
||||||
if(config.get().playerBlacklist.contains(player.getEntityName())) {
|
if(config.get().playerBlacklist.contains(player.getEntityName())) {
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} is already blacklisted.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 已经在黑名单中!", player.getEntityName());
|
||||||
} else {
|
} else {
|
||||||
config.get().playerBlacklist.add(player.getEntityName());
|
config.get().playerBlacklist.add(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("Player: ");
|
builder.append("玩家: ");
|
||||||
builder.append(player.getEntityName());
|
builder.append(player.getEntityName());
|
||||||
builder.append(" added to the blacklist");
|
builder.append(" 被添加到黑名单");
|
||||||
|
|
||||||
if(config.get().playerWhitelist.contains(player.getEntityName())){
|
if(config.get().playerWhitelist.contains(player.getEntityName())){
|
||||||
config.get().playerWhitelist.remove(player.getEntityName());
|
config.get().playerWhitelist.remove(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
builder.append(" and removed form the whitelist");
|
builder.append(" 并且被移除白名单");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(" successfully.");
|
builder.append(" 成功.");
|
||||||
|
|
||||||
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
||||||
|
|
||||||
|
@ -104,14 +104,14 @@ public class BlacklistCommand {
|
||||||
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
||||||
|
|
||||||
if(!config.get().playerBlacklist.contains(player.getEntityName())) {
|
if(!config.get().playerBlacklist.contains(player.getEntityName())) {
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} newer was blacklisted.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 还从未被列入黑名单.", player.getEntityName());
|
||||||
} else {
|
} else {
|
||||||
config.get().playerBlacklist.remove(player.getEntityName());
|
config.get().playerBlacklist.remove(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
|
|
||||||
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
||||||
|
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} removed from the blacklist successfully.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 被移除黑名单成功! ", player.getEntityName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -61,26 +61,26 @@ public class DeleteCommand {
|
||||||
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer()));
|
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer()));
|
||||||
|
|
||||||
RestoreableFile.applyOnFiles(root, Optional.empty(),
|
RestoreableFile.applyOnFiles(root, Optional.empty(),
|
||||||
e -> log.sendErrorAL(source, "An exception occurred while trying to delete a file!", e),
|
e -> log.sendErrorAL(source, "在尝试删除备份文件时发生了异常!", e),
|
||||||
stream -> stream.filter(f -> f.getCreationTime().equals(dateTime)).map(RestoreableFile::getFile).findFirst()
|
stream -> stream.filter(f -> f.getCreationTime().equals(dateTime)).map(RestoreableFile::getFile).findFirst()
|
||||||
).ifPresentOrElse(file -> {
|
).ifPresentOrElse(file -> {
|
||||||
if(Globals.INSTANCE.getLockedFile().filter(p -> p == file).isEmpty()) {
|
if(Globals.INSTANCE.getLockedFile().filter(p -> p == file).isEmpty()) {
|
||||||
try {
|
try {
|
||||||
Files.delete((Path) file);
|
Files.delete((Path) file);
|
||||||
log.sendInfo(source, "File {} successfully deleted!", file);
|
log.sendInfo(source, "备份: {} 被成功删除!", file);
|
||||||
|
|
||||||
if(Utilities.wasSentByPlayer(source))
|
if(Utilities.wasSentByPlayer(source))
|
||||||
log.info("Player {} deleted {}.", source.getPlayer().getName(), file);
|
log.info("玩家 {} 删除了备份: {}.", source.getPlayer().getName(), file);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.sendError(source, "Something went wrong while deleting file!");
|
log.sendError(source, "在尝试删除备份文件时发生了异常!");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.sendError(source, "Couldn't delete the file because it's being restored right now.");
|
log.sendError(source, "由于备份正在恢复中,无法删除该文件.");
|
||||||
log.sendHint(source, "If you want to abort restoration then use: /backup killR");
|
log.sendHint(source, "如果您想中止恢复过程,请使用以下命令:/backup killR");
|
||||||
}
|
}
|
||||||
}, () -> {
|
}, () -> {
|
||||||
log.sendInfo(source, "Couldn't find file by this name.");
|
log.sendInfo(source, "根据您提供的文件名找不到相应的文件.");
|
||||||
log.sendInfo(source, "Maybe try /backup list");
|
log.sendInfo(source, "也许您可以试试: /backup list");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -37,14 +37,14 @@ public class ListBackupsCommand {
|
||||||
var backups = RestoreHelper.getAvailableBackups(ctx.getSource().getServer());
|
var backups = RestoreHelper.getAvailableBackups(ctx.getSource().getServer());
|
||||||
|
|
||||||
if(backups.size() == 0) {
|
if(backups.size() == 0) {
|
||||||
builder.append("There a no backups available for this world.");
|
builder.append("该世界没有可用的备份文件. ");
|
||||||
} else if(backups.size() == 1) {
|
} else if(backups.size() == 1) {
|
||||||
builder.append("There is only one backup available: ");
|
builder.append("只有一个可用的备份文件: ");
|
||||||
builder.append(backups.get(0).toString());
|
builder.append(backups.get(0).toString());
|
||||||
} else {
|
} else {
|
||||||
backups.sort(null);
|
backups.sort(null);
|
||||||
Iterator<RestoreableFile> iterator = backups.iterator();
|
Iterator<RestoreableFile> iterator = backups.iterator();
|
||||||
builder.append("Available backups:\n");
|
builder.append("可用的备份文件:\n");
|
||||||
|
|
||||||
builder.append(iterator.next());
|
builder.append(iterator.next());
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class WhitelistCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int help(ServerCommandSource source){
|
private static int help(ServerCommandSource source){
|
||||||
log.sendInfo(source, "Available command are: add [player], remove [player], list.");
|
log.sendInfo(source, "可用的命令有: add [player], remove [player], list.");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class WhitelistCommand {
|
||||||
private static int executeList(ServerCommandSource source){
|
private static int executeList(ServerCommandSource source){
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("Currently on the whitelist are: ");
|
builder.append("目前在白名单的有: ");
|
||||||
|
|
||||||
for(String name : config.get().playerWhitelist){
|
for(String name : config.get().playerWhitelist){
|
||||||
builder.append(name);
|
builder.append(name);
|
||||||
|
@ -73,24 +73,24 @@ public class WhitelistCommand {
|
||||||
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
||||||
|
|
||||||
if(config.get().playerWhitelist.contains(player.getEntityName())) {
|
if(config.get().playerWhitelist.contains(player.getEntityName())) {
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} is already whitelisted.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 已经在白名单列表里.", player.getEntityName());
|
||||||
} else {
|
} else {
|
||||||
config.get().playerWhitelist.add(player.getEntityName());
|
config.get().playerWhitelist.add(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("Player: ");
|
builder.append("玩家: ");
|
||||||
builder.append(player.getEntityName());
|
builder.append(player.getEntityName());
|
||||||
builder.append(" added to the whitelist");
|
builder.append(" 被添加的白名单");
|
||||||
|
|
||||||
if(config.get().playerBlacklist.contains(player.getEntityName())){
|
if(config.get().playerBlacklist.contains(player.getEntityName())){
|
||||||
config.get().playerBlacklist.remove(player.getEntityName());
|
config.get().playerBlacklist.remove(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
builder.append(" and removed form the blacklist");
|
builder.append(" 并且被移除黑名单");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.append(" successfully.");
|
builder.append(" 成功.");
|
||||||
|
|
||||||
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
||||||
|
|
||||||
|
@ -104,14 +104,14 @@ public class WhitelistCommand {
|
||||||
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
||||||
|
|
||||||
if(!config.get().playerWhitelist.contains(player.getEntityName())) {
|
if(!config.get().playerWhitelist.contains(player.getEntityName())) {
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} newer was whitelisted.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 还从未被列入白名单.", player.getEntityName());
|
||||||
} else {
|
} else {
|
||||||
config.get().playerWhitelist.remove(player.getEntityName());
|
config.get().playerWhitelist.remove(player.getEntityName());
|
||||||
config.save();
|
config.save();
|
||||||
|
|
||||||
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
ctx.getSource().getServer().getCommandManager().sendCommandTree(player);
|
||||||
|
|
||||||
log.sendInfo(ctx.getSource(), "Player: {} removed from the whitelist successfully.", player.getEntityName());
|
log.sendInfo(ctx.getSource(), "玩家: {} 被移除白名单成功!", player.getEntityName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class KillRestoreCommand {
|
||||||
return CommandManager.literal("killR")
|
return CommandManager.literal("killR")
|
||||||
.executes(ctx -> {
|
.executes(ctx -> {
|
||||||
if(Globals.INSTANCE.getAwaitThread().filter(Thread::isAlive).isEmpty()) {
|
if(Globals.INSTANCE.getAwaitThread().filter(Thread::isAlive).isEmpty()) {
|
||||||
log.sendInfo(ctx.getSource(), "Failed to stop backup restoration");
|
log.sendInfo(ctx.getSource(), "无法停止备份恢复过程");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,13 +43,13 @@ public class KillRestoreCommand {
|
||||||
Globals.INSTANCE.globalShutdownBackupFlag.set(true);
|
Globals.INSTANCE.globalShutdownBackupFlag.set(true);
|
||||||
Globals.INSTANCE.setLockedFile(null);
|
Globals.INSTANCE.setLockedFile(null);
|
||||||
|
|
||||||
log.info("{} cancelled backup restoration.", Utilities.wasSentByPlayer(ctx.getSource()) ?
|
log.info("{} 备份恢复操作已被取消", Utilities.wasSentByPlayer(ctx.getSource()) ?
|
||||||
"Player: " + ctx.getSource().getName() :
|
"Player: " + ctx.getSource().getName() :
|
||||||
"SERVER"
|
"SERVER"
|
||||||
);
|
);
|
||||||
|
|
||||||
if(Utilities.wasSentByPlayer(ctx.getSource()))
|
if(Utilities.wasSentByPlayer(ctx.getSource()))
|
||||||
log.sendInfo(ctx.getSource(), "Backup restoration successfully stopped.");
|
log.sendInfo(ctx.getSource(), "备份恢复已成功停止. ");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,10 +63,10 @@ public class RestoreBackupCommand {
|
||||||
).executes(context -> {
|
).executes(context -> {
|
||||||
ServerCommandSource source = context.getSource();
|
ServerCommandSource source = context.getSource();
|
||||||
|
|
||||||
log.sendInfo(source, "To restore given backup you have to provide exact creation time in format:");
|
log.sendInfo(source, "要恢复给定的备份,您必须以以下格式提供准确的创建时间:");
|
||||||
log.sendInfo(source, "[YEAR]-[MONTH]-[DAY]_[HOUR].[MINUTE].[SECOND]");
|
log.sendInfo(source, "[年]-[月]-[日]_[小时].[分钟].[秒]");
|
||||||
log.sendInfo(source, "Example: /backup restore 2020-08-05_10.58.33");
|
log.sendInfo(source, "示例:/backup restore 2020-08-05_10.58.33");
|
||||||
log.sendInfo(source, "You may also type '/backup restore latest' to restore the freshest backup");
|
log.sendInfo(source, "您还可以输入 '/backup restore latest' 来恢复最新的备份。");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
|
@ -74,7 +74,7 @@ public class RestoreBackupCommand {
|
||||||
|
|
||||||
private static int execute(String file, @Nullable String comment, ServerCommandSource source) throws CommandSyntaxException {
|
private static int execute(String file, @Nullable String comment, ServerCommandSource source) throws CommandSyntaxException {
|
||||||
if(Globals.INSTANCE.getAwaitThread().filter(Thread::isAlive).isPresent()) {
|
if(Globals.INSTANCE.getAwaitThread().filter(Thread::isAlive).isPresent()) {
|
||||||
log.sendInfo(source, "Someone has already started another restoration.");
|
log.sendInfo(source, "已经有其他人开始了另一个恢复操作。");
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -96,10 +96,10 @@ public class RestoreBackupCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(backupFile.isEmpty()) {
|
if(backupFile.isEmpty()) {
|
||||||
log.sendInfo(source, "No file created on {} was found!", dateTime.format(Globals.defaultDateTimeFormatter));
|
log.sendInfo(source, "在{}上没有找到创建的文件!", dateTime.format(Globals.defaultDateTimeFormatter));
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
log.info("Found file to restore {}", backupFile.get().getFile().getFileName().toString());
|
log.info("找到要恢复的文件:{}", backupFile.get().getFile().getFileName().toString());
|
||||||
|
|
||||||
Globals.INSTANCE.setAwaitThread(
|
Globals.INSTANCE.setAwaitThread(
|
||||||
RestoreHelper.create(
|
RestoreHelper.create(
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class Cleanup implements Callable<Integer> {
|
||||||
final long now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
|
final long now = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
|
||||||
|
|
||||||
deletedFiles += RestoreableFile.applyOnFiles(root, 0L,
|
deletedFiles += RestoreableFile.applyOnFiles(root, 0L,
|
||||||
e -> log.error("An exception occurred while trying to delete old files!", e),
|
e -> log.error("尝试删除旧文件时发生异常!", e),
|
||||||
stream -> stream.filter(f -> now - f.getCreationTime().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
|
stream -> stream.filter(f -> now - f.getCreationTime().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
|
||||||
.filter(f -> deleteFile(f.getFile(), ctx))
|
.filter(f -> deleteFile(f.getFile(), ctx))
|
||||||
.count()
|
.count()
|
||||||
|
@ -72,7 +72,7 @@ public class Cleanup implements Callable<Integer> {
|
||||||
long n = counts[0], size = counts[1];
|
long n = counts[0], size = counts[1];
|
||||||
|
|
||||||
var it = RestoreableFile.applyOnFiles(root, null,
|
var it = RestoreableFile.applyOnFiles(root, null,
|
||||||
e -> log.error("An exception occurred while trying to delete old files!", e),
|
e -> log.error("尝试删除旧文件时发生异常!", e),
|
||||||
s -> s.sorted().toList().iterator());
|
s -> s.sorted().toList().iterator());
|
||||||
|
|
||||||
if(Objects.isNull(it)) return deletedFiles;
|
if(Objects.isNull(it)) return deletedFiles;
|
||||||
|
@ -104,13 +104,13 @@ public class Cleanup implements Callable<Integer> {
|
||||||
try {
|
try {
|
||||||
size += Files.size(f.getFile());
|
size += Files.size(f.getFile());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Couldn't get size of " + f.getFile(), e);
|
log.error("无法获取文件的大小 " + f.getFile(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while counting files!", e);
|
log.error("在计算文件数量时发生错误!", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new long[]{n, size};
|
return new long[]{n, size};
|
||||||
|
@ -128,8 +128,8 @@ public class Cleanup implements Callable<Integer> {
|
||||||
Files.delete(f);
|
Files.delete(f);
|
||||||
log.sendInfoAL(ctx, "Deleted: {}", f);
|
log.sendInfoAL(ctx, "Deleted: {}", f);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if(Utilities.wasSentByPlayer(ctx)) log.sendError(ctx, "Something went wrong while deleting: {}.", f);
|
if(Utilities.wasSentByPlayer(ctx)) log.sendError(ctx, "删除时发生了错误:{}.", f);
|
||||||
log.error("Something went wrong while deleting: {}.", f, e);
|
log.error("删除时发生了错误:{}.", f, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -25,6 +25,6 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class NoSpaceLeftOnDeviceException extends IOException {
|
public class NoSpaceLeftOnDeviceException extends IOException {
|
||||||
public NoSpaceLeftOnDeviceException(Throwable cause) {
|
public NoSpaceLeftOnDeviceException(Throwable cause) {
|
||||||
super("The underlying filesystem has ran out of available space.\nSee: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems", cause);
|
super("底层文件系统的可用空间已耗尽. \nSee: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems", cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,15 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
public void announce() {
|
public void announce() {
|
||||||
if(config.get().broadcastBackupStart) {
|
if(config.get().broadcastBackupStart) {
|
||||||
Utilities.notifyPlayers(server,
|
Utilities.notifyPlayers(server,
|
||||||
"Warning! Server backup will begin shortly. You may experience some lag."
|
"警告!服务器备份即将开始。您可能会遇到一些延迟."
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log.sendInfoAL(this, "Warning! Server backup will begin shortly. You may experience some lag.");
|
log.sendInfoAL(this, "Something went wrong while deleting: {}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("Backup started ");
|
builder.append("备份开始 ");
|
||||||
|
|
||||||
builder.append(initiator.getPrefix());
|
builder.append(initiator.getPrefix());
|
||||||
|
|
||||||
|
@ -65,13 +65,13 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
@Override
|
@Override
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
if (save) { //save the world
|
if (save) { //save the world
|
||||||
log.sendInfoAL(this, "Saving server...");
|
log.sendInfoAL(this, "保存世界中...");
|
||||||
server.saveAll(true, true, false);
|
server.saveAll(true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Path outFile = Utilities.getBackupRootPath(Utilities.getLevelName(server)).resolve(getFileName());
|
Path outFile = Utilities.getBackupRootPath(Utilities.getLevelName(server)).resolve(getFileName());
|
||||||
|
|
||||||
log.trace("Outfile is: {}", outFile);
|
log.trace("输出备份文件为: {}", outFile);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//I think I should synchronise these two next calls...
|
//I think I should synchronise these two next calls...
|
||||||
|
@ -80,11 +80,11 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
|
|
||||||
Globals.INSTANCE.updateTMPFSFlag(server);
|
Globals.INSTANCE.updateTMPFSFlag(server);
|
||||||
|
|
||||||
log.sendInfoAL(this, "Starting backup");
|
log.sendInfoAL(this, "开始备份");
|
||||||
|
|
||||||
Path world = Utilities.getWorldFolder(server);
|
Path world = Utilities.getWorldFolder(server);
|
||||||
|
|
||||||
log.trace("Minecraft world is: {}", world);
|
log.trace("Minecraft 存档目录: {}", world);
|
||||||
|
|
||||||
Files.createDirectories(outFile.getParent());
|
Files.createDirectories(outFile.getParent());
|
||||||
Files.createFile(outFile);
|
Files.createFile(outFile);
|
||||||
|
@ -95,15 +95,15 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
else
|
else
|
||||||
coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
|
coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors());
|
log.trace("正在使用{}个线程对{}进行压缩。可用核心数:{}", coreCount, Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
switch (config.get().format) {
|
switch (config.get().format) {
|
||||||
case ZIP -> {
|
case ZIP -> {
|
||||||
if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
|
if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
|
||||||
log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
|
log.trace("使用并行压缩器进行压缩。线程数:{}", coreCount);
|
||||||
ParallelZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
ParallelZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
||||||
} else {
|
} else {
|
||||||
log.trace("Using REGULAR Zip Compressor.");
|
log.trace("使用普通的Zip压缩器进行压缩 (单线程)");
|
||||||
ZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
ZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,23 +113,23 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
|
|
||||||
if(cleanup) new Cleanup(commandSource, Utilities.getLevelName(server)).call();
|
if(cleanup) new Cleanup(commandSource, Utilities.getLevelName(server)).call();
|
||||||
|
|
||||||
if (config.get().broadcastBackupDone) Utilities.notifyPlayers(server, "Done!");
|
if (config.get().broadcastBackupDone) Utilities.notifyPlayers(server, "完成!");
|
||||||
else log.sendInfoAL(this, "Done!");
|
else log.sendInfoAL(this, "完成!");
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
//ExecutorService swallows exception, so I need to catch everything
|
//ExecutorService swallows exception, so I need to catch everything
|
||||||
log.error("An exception occurred when trying to create a new backup file!", e);
|
log.error("在尝试创建新的备份文件时发生了异常!", e);
|
||||||
|
|
||||||
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) {
|
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) {
|
||||||
try {
|
try {
|
||||||
Files.delete(outFile);
|
Files.delete(outFile);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
log.error("An exception occurred while trying go delete: {}", outFile, ex);
|
log.error("在尝试删除{}时发生了异常!", outFile, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initiator == ActionInitiator.Player)
|
if (initiator == ActionInitiator.Player)
|
||||||
log.sendError(this, "An exception occurred when trying to create new backup file!");
|
log.sendError(this, "在尝试创建新的备份文件时发生了异常!");
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -215,11 +215,11 @@ public record ExecutableBackup(@NotNull MinecraftServer server,
|
||||||
public ExecutableBackup build() {
|
public ExecutableBackup build() {
|
||||||
if (guessInitiator) {
|
if (guessInitiator) {
|
||||||
initiator = Utilities.wasSentByPlayer(commandSource) ? ActionInitiator.Player : ActionInitiator.ServerConsole;
|
initiator = Utilities.wasSentByPlayer(commandSource) ? ActionInitiator.Player : ActionInitiator.ServerConsole;
|
||||||
} else if (initiator == null) throw new NoSuchElementException("No initiator provided!");
|
} else if (initiator == null) throw new NoSuchElementException("未提供发起者!");
|
||||||
|
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
if (commandSource != null) setServer(commandSource.getServer());
|
if (commandSource != null) setServer(commandSource.getServer());
|
||||||
else throw new RuntimeException("Neither MinecraftServer or ServerCommandSource were provided!");
|
else throw new RuntimeException("未提供MinecraftServer或ServerCommandSource!");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutableBackup v = new ExecutableBackup(server, commandSource, initiator, save, cleanup, comment, LocalDateTime.now());
|
ExecutableBackup v = new ExecutableBackup(server, commandSource, initiator, save, cleanup, comment, LocalDateTime.now());
|
||||||
|
|
|
@ -62,7 +62,7 @@ public record FileInputStreamSupplier(Path path, String name, FileTreeHashBuilde
|
||||||
try {
|
try {
|
||||||
return getInputStream();
|
return getInputStream();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("An exception occurred while trying to create an input stream from file: {}!", path.toString(), e);
|
log.error("尝试从文件{}创建输入流时发生了异常!", path.toString(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -76,7 +76,7 @@ public abstract class AbstractCompressor {
|
||||||
fileHashBuilder.update(file, 0, 0);
|
fileHashBuilder.update(file, 0, 0);
|
||||||
//In Permissive mode we allow partial backups
|
//In Permissive mode we allow partial backups
|
||||||
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) throw e;
|
if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) throw e;
|
||||||
else log.sendErrorAL(ctx, "An exception occurred while trying to compress: {}",
|
else log.sendErrorAL(ctx, "在尝试压缩{}时发生了异常!",
|
||||||
inputFile.relativize(file).toString(), e
|
inputFile.relativize(file).toString(), e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public abstract class AbstractCompressor {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.sendInfoAL(ctx, "Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
log.sendInfoAL(ctx, "压缩耗时:{}秒. ", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException;
|
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException;
|
||||||
|
|
|
@ -59,11 +59,11 @@ public class FileTreeHashBuilder {
|
||||||
public long getValue(boolean lock) throws InterruptedException {
|
public long getValue(boolean lock) throws InterruptedException {
|
||||||
long leftover = latch.getCount();
|
long leftover = latch.getCount();
|
||||||
if(lock) latch.await();
|
if(lock) latch.await();
|
||||||
else if(leftover != 0) log.warn("Finishing with {} files unprocessed!", leftover);
|
else if(leftover != 0) log.warn("处理中,剩余{}个文件未处理!", leftover);
|
||||||
|
|
||||||
var hasher = Globals.CHECKSUM_SUPPLIER.get();
|
var hasher = Globals.CHECKSUM_SUPPLIER.get();
|
||||||
|
|
||||||
log.debug("Closing: files: {}, bytes {}, raw hash {}", filesProcessed, filesTotalSize, hash);
|
log.debug("文件数:{},字节数:{},原始哈希值:{}", filesProcessed, filesTotalSize, hash);
|
||||||
hasher.update(hash);
|
hasher.update(hash);
|
||||||
hasher.update(filesProcessed);
|
hasher.update(filesProcessed);
|
||||||
hasher.update(filesTotalSize);
|
hasher.update(filesTotalSize);
|
||||||
|
|
|
@ -42,13 +42,13 @@ public class AwaitThread extends Thread {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
log.info("Countdown begins... Waiting {} second.", delay);
|
log.info("开始倒计时...等待{}秒.", delay);
|
||||||
|
|
||||||
// 𝄞 This is final count down! Tu ruru Tu, Tu Ru Tu Tu ♪
|
// 𝄞 This is final count down! Tu ruru Tu, Tu Ru Tu Tu ♪
|
||||||
try {
|
try {
|
||||||
Thread.sleep(delay * 1000L);
|
Thread.sleep(delay * 1000L);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
log.info("Backup restoration cancelled.");
|
log.info("备份恢复已取消.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
Globals.INSTANCE.globalShutdownBackupFlag.set(false);
|
Globals.INSTANCE.globalShutdownBackupFlag.set(false);
|
||||||
|
|
||||||
log.info("Shutting down server...");
|
log.info("关闭服务器...");
|
||||||
|
|
||||||
ctx.server().stop(false);
|
ctx.server().stop(false);
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
ctx.restoreableFile().getFile().getFileName().toString()
|
ctx.restoreableFile().getFile().getFileName().toString()
|
||||||
);
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("An exception occurred while unpacking backup", e);
|
log.error("在解压备份时发生了异常.", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
new Thread(waitForShutdown, "Server shutdown wait thread").start();
|
new Thread(waitForShutdown, "Server shutdown wait thread").start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info("Starting decompression...");
|
log.info("开始解压...");
|
||||||
|
|
||||||
long hash;
|
long hash;
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
else
|
else
|
||||||
hash = GenericTarDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
|
hash = GenericTarDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
|
||||||
|
|
||||||
log.info("Waiting for server to fully terminate...");
|
log.info("等待服务器完全终止...");
|
||||||
|
|
||||||
//locks until the backup is finished and the server is dead
|
//locks until the backup is finished and the server is dead
|
||||||
waitForShutdown.get();
|
waitForShutdown.get();
|
||||||
|
@ -110,11 +110,11 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
Optional<String> errorMsg;
|
Optional<String> errorMsg;
|
||||||
|
|
||||||
if(Files.notExists(CompressionStatus.resolveStatusFilename(tmp))) {
|
if(Files.notExists(CompressionStatus.resolveStatusFilename(tmp))) {
|
||||||
errorMsg = Optional.of("Status file not found!");
|
errorMsg = Optional.of("未找到状态文件!");
|
||||||
} else {
|
} else {
|
||||||
CompressionStatus status = CompressionStatus.readFromFile(tmp);
|
CompressionStatus status = CompressionStatus.readFromFile(tmp);
|
||||||
|
|
||||||
log.info("Status: {}", status);
|
log.info("状态: {}", status);
|
||||||
|
|
||||||
Files.delete(tmp.resolve(CompressionStatus.DATA_FILENAME));
|
Files.delete(tmp.resolve(CompressionStatus.DATA_FILENAME));
|
||||||
|
|
||||||
|
@ -122,8 +122,8 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errorMsg.isEmpty() || !config.get().integrityVerificationMode.verify()) {
|
if(errorMsg.isEmpty() || !config.get().integrityVerificationMode.verify()) {
|
||||||
if (errorMsg.isEmpty()) log.info("Backup valid. Restoring");
|
if (errorMsg.isEmpty()) log.info("备份验证有效, 正在恢复.");
|
||||||
else log.info("Backup is damaged, but verification is disabled [{}]. Restoring", errorMsg.get());
|
else log.info("备份已损坏,但验证已禁用[{}]。正在恢复. ", errorMsg.get());
|
||||||
|
|
||||||
//Disables write lock to override world file
|
//Disables write lock to override world file
|
||||||
((MinecraftServerSessionAccessor) ctx.server()).getSession().close();
|
((MinecraftServerSessionAccessor) ctx.server()).getSession().close();
|
||||||
|
@ -132,7 +132,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
Files.move(tmp, worldFile);
|
Files.move(tmp, worldFile);
|
||||||
|
|
||||||
if (config.get().deleteOldBackupAfterRestore) {
|
if (config.get().deleteOldBackupAfterRestore) {
|
||||||
log.info("Deleting restored backup file");
|
log.info("正在删除恢复的备份文件.");
|
||||||
Files.delete(ctx.restoreableFile().getFile());
|
Files.delete(ctx.restoreableFile().getFile());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,7 +140,7 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("An exception occurred while trying to restore a backup!", e);
|
log.error("在尝试恢复备份时发生了异常!", e);
|
||||||
} finally {
|
} finally {
|
||||||
//Regardless of what happened, we should still clean up
|
//Regardless of what happened, we should still clean up
|
||||||
if(Files.exists(tmp)) {
|
if(Files.exists(tmp)) {
|
||||||
|
@ -153,6 +153,6 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
//in case we're playing on client
|
//in case we're playing on client
|
||||||
Globals.INSTANCE.globalShutdownBackupFlag.set(true);
|
Globals.INSTANCE.globalShutdownBackupFlag.set(true);
|
||||||
|
|
||||||
log.info("Done!");
|
log.info("完成!");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -41,7 +41,7 @@ public class RestoreHelper {
|
||||||
|
|
||||||
Optional<RestoreableFile> optionalFile =
|
Optional<RestoreableFile> optionalFile =
|
||||||
RestoreableFile.applyOnFiles(root, Optional.empty(),
|
RestoreableFile.applyOnFiles(root, Optional.empty(),
|
||||||
e -> log.error("An exception occurred while trying to lock the file!", e),
|
e -> log.error("在尝试锁定文件时发生了异常!", e),
|
||||||
s -> s.filter(rf -> rf.getCreationTime().equals(backupTime))
|
s -> s.filter(rf -> rf.getCreationTime().equals(backupTime))
|
||||||
.findFirst());
|
.findFirst());
|
||||||
|
|
||||||
|
@ -63,13 +63,12 @@ public class RestoreHelper {
|
||||||
|
|
||||||
public static AwaitThread create(RestoreContext ctx) {
|
public static AwaitThread create(RestoreContext ctx) {
|
||||||
if(ctx.initiator() == ActionInitiator.Player)
|
if(ctx.initiator() == ActionInitiator.Player)
|
||||||
log.info("Backup restoration was initiated by: {}", ctx.commandSource().getName());
|
log.info("备份恢复由以下玩家发起:{}", ctx.commandSource().getName());
|
||||||
else
|
else
|
||||||
log.info("Backup restoration was initiated form Server Console");
|
log.info("备份恢复由服务器控制台发起");
|
||||||
|
|
||||||
Utilities.notifyPlayers(
|
Utilities.notifyPlayers(
|
||||||
ctx.server(),
|
ctx.server(),
|
||||||
"Warning! The server is going to shut down in " + config.get().restoreDelay + " seconds!"
|
"警告!服务器将在" + config.get().restoreDelay + "秒后关闭!"
|
||||||
);
|
);
|
||||||
|
|
||||||
return new AwaitThread(
|
return new AwaitThread(
|
||||||
|
@ -82,7 +81,7 @@ public class RestoreHelper {
|
||||||
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
||||||
|
|
||||||
return RestoreableFile.applyOnFiles(root, new LinkedList<>(),
|
return RestoreableFile.applyOnFiles(root, new LinkedList<>(),
|
||||||
e -> log.error("Error while listing available backups", e),
|
e -> log.error("列出可用备份时发生错误.", e),
|
||||||
s -> s.sorted().collect(Collectors.toCollection(LinkedList::new)));
|
s -> s.sorted().collect(Collectors.toCollection(LinkedList::new)));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -68,7 +68,7 @@ public class GenericTarDecompressor {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
log.info("解压缩耗时{}秒. ", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return treeBuilder.getValue(false);
|
return treeBuilder.getValue(false);
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class ZipDecompressor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
log.info("解压缩耗时{}秒。", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return hashBuilder.getValue(false);
|
return hashBuilder.getValue(false);
|
||||||
|
|
Loading…
Reference in New Issue