diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/CreateWorldCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/CreateWorldCommand.java index eb708a55bc..d439a8236b 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/CreateWorldCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/CreateWorldCommand.java @@ -26,17 +26,17 @@ public class CreateWorldCommand extends AbstractCommand implements Holdable { public CreateWorldCommand() { setName("createworld"); - setSyntax("createworld [] (generator:) (worldtype:) (environment:) (copy_from:) (seed:) (settings:) (generate_structures:true/false)"); - setRequiredArguments(1, 8); - setPrefixesHandled("generator", "worldtype", "environment", "copy_from", "seed", "settings", "generate_structures"); + setSyntax("createworld [] (generator:) (worldtype:) (environment:) (copy_from:) (seed:) (settings:) (generate_structures:true/false) (copy_to:)"); + setRequiredArguments(1, 9); + setPrefixesHandled("generator", "worldtype", "environment", "copy_from", "seed", "settings", "generate_structures", "copy_to"); isProcedural = false; } // <--[command] // @Name CreateWorld - // @Syntax createworld [] (generator:) (worldtype:) (environment:) (copy_from:) (seed:) (settings:) (generate_structures:true/false) + // @Syntax createworld [] (generator:) (worldtype:) (environment:) (copy_from:) (seed:) (settings:) (generate_structures:true/false) (copy_to:) // @Required 1 - // @Maximum 8 + // @Maximum 9 // @Short Creates a new world, or loads an existing world. // @Synonyms LoadWorld // @Group world @@ -60,6 +60,8 @@ public CreateWorldCommand() { // Optionally specify an existing world to copy files from. // The 'copy_from' argument is ~waitable. Refer to <@link language ~waitable>. // + // Optionally specify a 'copy_to' path, which overrides the folder name the files are copied into (useful for datapack dimensions). + // // It's often ideal to put this command inside <@link event server prestart>. // // @Tags @@ -121,17 +123,21 @@ public void execute(ScriptEntry scriptEntry) { ElementTag worldType = scriptEntry.argForPrefixAsElement("worldtype", "NORMAL"); ElementTag environment = scriptEntry.argForPrefixAsElement("environment", "NORMAL"); ElementTag copy_from = scriptEntry.argForPrefixAsElement("copy_from", null); + ElementTag copy_to = scriptEntry.argForPrefixAsElement("copy_to", null); ElementTag settings = scriptEntry.argForPrefixAsElement("settings", null); ElementTag seed = scriptEntry.argForPrefixAsElement("seed", null); ElementTag generateStructures = scriptEntry.argForPrefixAsElement("generate_structures", null); if (scriptEntry.dbCallShouldDebug()) { - Debug.report(scriptEntry, getName(), worldName, generator, environment, copy_from, settings, worldType, seed, generateStructures); + Debug.report(scriptEntry, getName(), worldName, generator, environment, copy_from, copy_to, settings, worldType, seed, generateStructures); } if (Bukkit.getWorld(worldName.asString()) != null) { Debug.echoDebug(scriptEntry, "CreateWorld doing nothing, world by that name already loaded."); scriptEntry.setFinished(true); return; } + String targetFolder = copy_to != null ? copy_to.asString() : worldName.asString(); + String sourceFolder = copy_from != null ? copy_from.asString().replace("w@", "") : null; + if (!Settings.cache_createWorldSymbols) { if (forbiddenSymbols.containsAnyMatch(worldName.asString())) { Debug.echoError("Cannot use world names with non-alphanumeric symbols due to security settings in Denizen/config.yml."); @@ -140,7 +146,7 @@ public void execute(ScriptEntry scriptEntry) { } } else if (!Settings.cache_createWorldWeirdPaths) { - String cleaned = worldName.asLowerString().replace('\\', '/'); + String cleaned = targetFolder.toLowerCase().replace('\\', '/'); while (cleaned.contains("//")) { cleaned = cleaned.replace("//", "/"); } @@ -157,8 +163,28 @@ else if (!Settings.cache_createWorldWeirdPaths) { scriptEntry.setFinished(true); return; } + + if (sourceFolder != null) { + String cleanedSource = sourceFolder.toLowerCase().replace('\\', '/'); + while (cleanedSource.contains("//")) { + cleanedSource = cleanedSource.replace("//", "/"); + } + if (cleanedSource.startsWith("/")) { + cleanedSource = cleanedSource.substring(1); + } + if (cleanedSource.startsWith("plugins/")) { + Debug.echoError("CreateWorld cannot copy from a folder inside plugins due to security settings in Denizen/config.yml."); + scriptEntry.setFinished(true); + return; + } + if (cleanedSource.startsWith("..")) { + Debug.echoError("CreateWorld cannot copy from a world with a raised path (contains '..') due to security settings in Denizen/config.yml."); + scriptEntry.setFinished(true); + return; + } + } } - final File newFolder = new File(Bukkit.getWorldContainer(), worldName.asString()); + final File newFolder = new File(Bukkit.getWorldContainer(), targetFolder); if (!Utilities.canWriteToFile(newFolder)) { Debug.echoError("Cannot copy to that new folder path due to security settings in Denizen/config.yml."); scriptEntry.setFinished(true); @@ -175,14 +201,10 @@ else if (!Settings.cache_createWorldWeirdPaths) { scriptEntry.setFinished(true); return; } - if (copy_from != null && !Settings.cache_createWorldSymbols && forbiddenSymbols.containsAnyMatch(copy_from.asString())) { - Debug.echoError("Cannot use copy_from world names with non-alphanumeric symbols due to security settings in Denizen/config.yml."); - scriptEntry.setFinished(true); - return; - } + Supplier copyRunnable = () -> { try { - File folder = new File(Bukkit.getWorldContainer(), copy_from.asString().replace("w@", "")); + File folder = new File(Bukkit.getWorldContainer(), sourceFolder); if (!Utilities.canReadFile(folder)) { Debug.echoError(scriptEntry, "Cannot copy from that folder path due to security settings in Denizen/config.yml."); return false; @@ -197,11 +219,11 @@ else if (!Settings.cache_createWorldWeirdPaths) { } CoreUtilities.copyDirectory(folder, newFolder, excludedExtensionsForCopyFrom); Debug.echoDebug(scriptEntry, "Copied " + folder.getName() + " to " + newFolder.getName()); - File file = new File(Bukkit.getWorldContainer(), worldName.asString() + "/uid.dat"); + File file = new File(Bukkit.getWorldContainer(), targetFolder + "/uid.dat"); if (file.exists()) { file.delete(); } - File file2 = new File(Bukkit.getWorldContainer(), worldName.asString() + "/session.lock"); + File file2 = new File(Bukkit.getWorldContainer(), targetFolder + "/session.lock"); if (file2.exists()) { file2.delete(); }