Better error handling of world folder deletion

This commit is contained in:
Ben Woo 2023-09-06 00:13:53 +08:00
parent d01a884025
commit f056dcdbc9
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
5 changed files with 70 additions and 11 deletions

View File

@ -101,6 +101,7 @@ public enum MVCorei18n implements MessageKeyProvider {
DELETEWORLD_DELETED, DELETEWORLD_DELETED,
DELETEWORLD_WORLDNONEXISTENT, DELETEWORLD_WORLDNONEXISTENT,
DELETEWORLD_LOADFAILED,
DELETEWORLD_WORLDFOLDERNOTFOUND, DELETEWORLD_WORLDFOLDERNOTFOUND,
DELETEWORLD_FAILEDTODELETEFOLDER, DELETEWORLD_FAILEDTODELETEFOLDER,

View File

@ -10,6 +10,7 @@ import com.onarandombox.MultiverseCore.utils.result.Result;
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig; import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig;
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigManager; import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigManager;
import com.onarandombox.MultiverseCore.worldnew.generators.GeneratorProvider; import com.onarandombox.MultiverseCore.worldnew.generators.GeneratorProvider;
import com.onarandombox.MultiverseCore.worldnew.helpers.FilesManipulator;
import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions; import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions; import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.results.CreateWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.CreateWorldResult;
@ -52,6 +53,7 @@ public class WorldManager {
private final WorldsConfigManager worldsConfigManager; private final WorldsConfigManager worldsConfigManager;
private final WorldNameChecker worldNameChecker; private final WorldNameChecker worldNameChecker;
private final GeneratorProvider generatorProvider; private final GeneratorProvider generatorProvider;
private final FilesManipulator filesManipulator;
private final BlockSafety blockSafety; private final BlockSafety blockSafety;
private final SafeTTeleporter safeTTeleporter; private final SafeTTeleporter safeTTeleporter;
private final LocationManipulation locationManipulation; private final LocationManipulation locationManipulation;
@ -61,6 +63,7 @@ public class WorldManager {
@NotNull WorldsConfigManager worldsConfigManager, @NotNull WorldsConfigManager worldsConfigManager,
@NotNull WorldNameChecker worldNameChecker, @NotNull WorldNameChecker worldNameChecker,
@NotNull GeneratorProvider generatorProvider, @NotNull GeneratorProvider generatorProvider,
@NotNull FilesManipulator filesManipulator,
@NotNull BlockSafety blockSafety, @NotNull BlockSafety blockSafety,
@NotNull SafeTTeleporter safeTTeleporter, @NotNull SafeTTeleporter safeTTeleporter,
@NotNull LocationManipulation locationManipulation @NotNull LocationManipulation locationManipulation
@ -73,6 +76,7 @@ public class WorldManager {
this.worldsConfigManager = worldsConfigManager; this.worldsConfigManager = worldsConfigManager;
this.worldNameChecker = worldNameChecker; this.worldNameChecker = worldNameChecker;
this.generatorProvider = generatorProvider; this.generatorProvider = generatorProvider;
this.filesManipulator = filesManipulator;
this.blockSafety = blockSafety; this.blockSafety = blockSafety;
this.safeTTeleporter = safeTTeleporter; this.safeTTeleporter = safeTTeleporter;
this.locationManipulation = locationManipulation; this.locationManipulation = locationManipulation;
@ -396,18 +400,38 @@ public class WorldManager {
} }
/** /**
* Deletes an existing multiverse world entirely. Warning: This will delete all world files. * Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded.
* Warning: This will delete all world files.
* *
* @param worldName The name of the world to delete. * @param worldName The name of the world to delete.
* @return The result of the delete action. * @return The result of the delete action.
*/ */
public Result<DeleteWorldResult.Success, DeleteWorldResult.Failure> deleteWorld(@NotNull String worldName) { public Result<DeleteWorldResult.Success, DeleteWorldResult.Failure> deleteWorld(@NotNull String worldName) {
// TODO: Attempt to load offline world return getOfflineWorld(worldName)
return getMVWorld(worldName)
.map(this::deleteWorld) .map(this::deleteWorld)
.getOrElse(() -> Result.failure(DeleteWorldResult.Failure.WORLD_NON_EXISTENT, replace("{world}").with(worldName))); .getOrElse(() -> Result.failure(DeleteWorldResult.Failure.WORLD_NON_EXISTENT, replace("{world}").with(worldName)));
} }
/**
* Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded.
* Warning: This will delete all world files.
*
* @param world The offline world to delete.
* @return The result of the delete action.
*/
public Result<DeleteWorldResult.Success, DeleteWorldResult.Failure> deleteWorld(@NotNull OfflineWorld world) {
return getMVWorld(world).fold(
() -> {
var result = loadWorld(world);
if (result.isFailure()) {
return Result.failure(DeleteWorldResult.Failure.LOAD_FAILED, replace("{world}").with(world.getName()));
}
return deleteWorld(world);
},
this::deleteWorld
);
}
/** /**
* Deletes an existing multiverse world entirely. Warning: This will delete all world files. * Deletes an existing multiverse world entirely. Warning: This will delete all world files.
* *
@ -427,13 +451,14 @@ public class WorldManager {
} }
// Erase world files from disk // Erase world files from disk
// TODO: Config options to keep certain files // TODO: Possible config options to keep certain files
if (!FileUtils.deleteFolder(worldFolder)) { return filesManipulator.deleteFolder(worldFolder).fold(
Logging.severe("Failed to delete world folder: " + worldFolder); (exception) -> Result.failure(DeleteWorldResult.Failure.FAILED_TO_DELETE_FOLDER,
return Result.failure(DeleteWorldResult.Failure.FAILED_TO_DELETE_FOLDER, replace("{world}").with(world.getName())); replace("{world}").with(world.getName()),
} replace("{error}").with(exception.getMessage())
),
return Result.success(DeleteWorldResult.Success.DELETED, replace("{world}").with(world.getName())); (success) -> Result.success(DeleteWorldResult.Success.DELETED, replace("{world}").with(world.getName()))
);
} }
/** /**

View File

@ -0,0 +1,31 @@
package com.onarandombox.MultiverseCore.worldnew.helpers;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;
@Service
public class FilesManipulator {
public Try<Void> deleteFolder(File file) {
return deleteFolder(file.toPath());
}
public Try<Void> deleteFolder(Path path) {
try (Stream<Path> files = Files.walk(path)) {
files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
return Try.success(null);
} catch (IOException e) {
Logging.severe("Failed to delete folder: " + path.toAbsolutePath());
e.printStackTrace();
return Try.failure(e);
}
}
}

View File

@ -25,6 +25,7 @@ public class DeleteWorldResult {
public enum Failure implements FailureReason { public enum Failure implements FailureReason {
WORLD_NON_EXISTENT(MVCorei18n.DELETEWORLD_WORLDNONEXISTENT), WORLD_NON_EXISTENT(MVCorei18n.DELETEWORLD_WORLDNONEXISTENT),
LOAD_FAILED(MVCorei18n.DELETEWORLD_LOADFAILED),
WORLD_FOLDER_NOT_FOUND(MVCorei18n.DELETEWORLD_WORLDFOLDERNOTFOUND), WORLD_FOLDER_NOT_FOUND(MVCorei18n.DELETEWORLD_WORLDFOLDERNOTFOUND),
REMOVE_FAILED(null), REMOVE_FAILED(null),
FAILED_TO_DELETE_FOLDER(MVCorei18n.DELETEWORLD_FAILEDTODELETEFOLDER), FAILED_TO_DELETE_FOLDER(MVCorei18n.DELETEWORLD_FAILEDTODELETEFOLDER),

View File

@ -137,8 +137,9 @@ mv-core.createworld.bukkitcreationfailed=Bukkit failed to create world '{world}'
mv-core.deleteworld.deleted=&aWorld '{world}' deleted! mv-core.deleteworld.deleted=&aWorld '{world}' deleted!
mv-core.deleteworld.worldnonexistent=World '{world}' not found! mv-core.deleteworld.worldnonexistent=World '{world}' not found!
mv-core.deleteworld.loadfailed=Unable to load world '{world}', does the world folder exist?&f You can run '&a/mv remove {world}&f' to remove it from Multiverse, or attempt to load again with '&a/mv load {world}&f'.
mv-core.deleteworld.worldfoldernotfound=World '{world}' folder not found! mv-core.deleteworld.worldfoldernotfound=World '{world}' folder not found!
mv-core.deleteworld.failedtodeletefolder=Failed to delete world '{world}' folder!& fSee console for more details. mv-core.deleteworld.failedtodeletefolder=Failed to delete world folder '{world}': {error}\n&fSee console for more details.
mv-core.importworld.imported=&aWorld '{world}' imported! mv-core.importworld.imported=&aWorld '{world}' imported!
mv-core.importworld.invalidworldname=World '{world}' contains invalid characters! mv-core.importworld.invalidworldname=World '{world}' contains invalid characters!