Skip to content

Commit afa2825

Browse files
authored
Merge pull request #3315 from Multiverse/feat/load-folder-check
Add folder checking before loading world
2 parents a771652 + 21b4f6b commit afa2825

File tree

6 files changed

+185
-32
lines changed

6 files changed

+185
-32
lines changed

src/main/java/org/mvplugins/multiverse/core/commands/LoadCommand.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
import co.aikar.commands.annotation.CommandAlias;
44
import co.aikar.commands.annotation.CommandCompletion;
55
import co.aikar.commands.annotation.CommandPermission;
6-
import co.aikar.commands.annotation.Conditions;
76
import co.aikar.commands.annotation.Description;
8-
import co.aikar.commands.annotation.Single;
7+
import co.aikar.commands.annotation.Optional;
98
import co.aikar.commands.annotation.Subcommand;
109
import co.aikar.commands.annotation.Syntax;
1110
import com.dumptruckman.minecraft.util.Logging;
@@ -15,19 +14,26 @@
1514

1615
import org.mvplugins.multiverse.core.command.LegacyAliasCommand;
1716
import org.mvplugins.multiverse.core.command.MVCommandIssuer;
18-
import org.mvplugins.multiverse.core.command.MVCommandManager;
17+
import org.mvplugins.multiverse.core.command.flag.CommandFlag;
18+
import org.mvplugins.multiverse.core.command.flag.CommandFlagsManager;
19+
import org.mvplugins.multiverse.core.command.flag.ParsedCommandFlags;
20+
import org.mvplugins.multiverse.core.command.flags.RemovePlayerFlags;
1921
import org.mvplugins.multiverse.core.locale.MVCorei18n;
2022
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
23+
import org.mvplugins.multiverse.core.world.MultiverseWorld;
2124
import org.mvplugins.multiverse.core.world.WorldManager;
25+
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions;
2226

2327
@Service
2428
class LoadCommand extends CoreCommand {
2529

2630
private final WorldManager worldManager;
31+
private final LoadCommand.Flags flags;
2732

2833
@Inject
29-
LoadCommand(@NotNull WorldManager worldManager) {
34+
LoadCommand(@NotNull WorldManager worldManager, @NotNull Flags flags) {
3035
this.worldManager = worldManager;
36+
this.flags = flags;
3137
}
3238

3339
@Subcommand("load")
@@ -38,13 +44,19 @@ class LoadCommand extends CoreCommand {
3844
void onLoadCommand(
3945
MVCommandIssuer issuer,
4046

41-
@Single
42-
@Conditions("worldname:scope=unloaded")
4347
@Syntax("<world>")
4448
@Description("{@@mv-core.load.world.description}")
45-
String worldName) {
46-
issuer.sendInfo(MVCorei18n.LOAD_LOADING, Replace.WORLD.with(worldName));
47-
worldManager.loadWorld(worldName)
49+
MultiverseWorld world,
50+
51+
@Optional
52+
@Syntax("[--remove-players]")
53+
@Description("")
54+
String[] flagArray) {
55+
ParsedCommandFlags parsedFlags = flags.parse(flagArray);
56+
57+
issuer.sendInfo(MVCorei18n.LOAD_LOADING, Replace.WORLD.with(world.getName()));
58+
worldManager.loadWorld(LoadWorldOptions.world(world)
59+
.doFolderCheck(!parsedFlags.hasFlag(flags.skipFolderCheck)))
4860
.onSuccess(newWorld -> {
4961
Logging.fine("World load success: " + newWorld);
5062
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS, Replace.WORLD.with(newWorld.getName()));
@@ -54,17 +66,32 @@ void onLoadCommand(
5466
});
5567
}
5668

69+
@Service
70+
private static final class Flags extends RemovePlayerFlags {
71+
72+
private static final String NAME = "mvunload";
73+
74+
@Inject
75+
private Flags(@NotNull CommandFlagsManager flagsManager) {
76+
super(NAME, flagsManager);
77+
}
78+
79+
private final CommandFlag skipFolderCheck = flag(CommandFlag.builder("--skip-folder-check")
80+
.addAlias("-f")
81+
.build());
82+
}
83+
5784
@Service
5885
private static final class LegacyAlias extends LoadCommand implements LegacyAliasCommand {
5986
@Inject
60-
LegacyAlias(@NotNull WorldManager worldManager) {
61-
super(worldManager);
87+
LegacyAlias(@NotNull WorldManager worldManager, @NotNull Flags flags) {
88+
super(worldManager, flags);
6289
}
6390

6491
@Override
6592
@CommandAlias("mvload")
66-
void onLoadCommand(MVCommandIssuer issuer, String worldName) {
67-
super.onLoadCommand(issuer, worldName);
93+
void onLoadCommand(MVCommandIssuer issuer, MultiverseWorld world, String[] flagArray) {
94+
super.onLoadCommand(issuer, world, flagArray);
6895
}
6996
}
7097
}

src/main/java/org/mvplugins/multiverse/core/listeners/MVWorldListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.mvplugins.multiverse.core.dynamiclistener.annotations.DefaultEventPriority;
1919
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
2020
import org.mvplugins.multiverse.core.world.WorldManager;
21+
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions;
2122
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions;
2223
import org.mvplugins.multiverse.core.world.reasons.LoadFailureReason;
2324
import org.mvplugins.multiverse.core.world.reasons.UnloadFailureReason;
@@ -68,7 +69,7 @@ void worldLoad(WorldLoadEvent event) {
6869
worldManager.getUnloadedWorld(event.getWorld().getName())
6970
.peek(world -> {
7071
Logging.fine("Loading world: " + world.getName());
71-
worldManager.loadWorld(world).onFailure(failure -> {
72+
worldManager.loadWorld(LoadWorldOptions.world(world)).onFailure(failure -> {
7273
if (failure.getFailureReason() != LoadFailureReason.WORLD_ALREADY_LOADING) {
7374
Logging.severe("Failed to load world: " + failure);
7475
}

src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.mvplugins.multiverse.core.world.options.DeleteWorldOptions;
6565
import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
6666
import org.mvplugins.multiverse.core.world.options.KeepWorldSettingsOptions;
67+
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions;
6768
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions;
6869
import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions;
6970
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions;
@@ -226,8 +227,9 @@ private void autoLoadWorlds() {
226227
if (isLoadedWorld(world) || !world.isAutoLoad()) {
227228
return;
228229
}
229-
loadWorld(world).onFailure(failure -> Logging.severe("Failed to load world %s: %s",
230-
world.getName(), failure));
230+
loadWorld(LoadWorldOptions.world(world))
231+
.onFailure(failure ->
232+
Logging.severe("Failed to load world %s: %s", world.getName(), failure));
231233
});
232234
}
233235

@@ -403,10 +405,14 @@ private void setDefaultEnvironmentScale(MultiverseWorld world) {
403405
*
404406
* @param worldName The name of the world to load.
405407
* @return The result of the load.
408+
*
409+
* @deprecated Use {@link #loadWorld(LoadWorldOptions)} instead.
406410
*/
411+
@Deprecated(since = "5.2", forRemoval = true)
412+
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
407413
public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull String worldName) {
408414
return getWorld(worldName)
409-
.map(this::loadWorld)
415+
.map(world -> loadWorld(LoadWorldOptions.world(world)))
410416
.getOrElse(() -> worldNameChecker.isValidWorldFolder(worldName)
411417
? worldActionResult(LoadFailureReason.WORLD_EXIST_FOLDER, worldName)
412418
: worldActionResult(LoadFailureReason.WORLD_NON_EXISTENT, worldName));
@@ -417,12 +423,30 @@ public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull Stri
417423
*
418424
* @param world The world to load.
419425
* @return The result of the load.
426+
*
427+
* @deprecated Use {@link #loadWorld(LoadWorldOptions)} instead.
420428
*/
429+
@Deprecated(since = "5.2", forRemoval = true)
430+
@ApiStatus.ScheduledForRemoval(inVersion = "6.0")
421431
public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull MultiverseWorld world) {
422-
return validateWorldToLoad(world).mapAttempt(this::doLoadWorld);
432+
return loadWorld(LoadWorldOptions.world(world));
423433
}
424434

425-
private Attempt<MultiverseWorld, LoadFailureReason> validateWorldToLoad(@NotNull MultiverseWorld mvWorld) {
435+
/**
436+
* Loads an existing world in config.
437+
*
438+
* @param options The options for customizing the loading of a world.
439+
* @return The result of the load.
440+
*
441+
* @since 5.2
442+
*/
443+
@ApiStatus.AvailableSince("5.2")
444+
public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull LoadWorldOptions options) {
445+
return validateWorldToLoad(options).mapAttempt(this::doLoadWorld);
446+
}
447+
448+
private Attempt<LoadWorldOptions, LoadFailureReason> validateWorldToLoad(@NotNull LoadWorldOptions options) {
449+
MultiverseWorld mvWorld = options.world();
426450
if (loadTracker.contains(mvWorld.getName())) {
427451
// This is to prevent recursive calls by WorldLoadEvent
428452
Logging.fine("World already loading: " + mvWorld.getName());
@@ -431,21 +455,19 @@ private Attempt<MultiverseWorld, LoadFailureReason> validateWorldToLoad(@NotNull
431455
Logging.severe("World already loaded: " + mvWorld.getName());
432456
return worldActionResult(LoadFailureReason.WORLD_EXIST_LOADED, mvWorld.getName());
433457
}
434-
return worldActionResult(mvWorld);
458+
return worldActionResult(options);
435459
}
436460

437-
private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull MultiverseWorld mvWorld) {
461+
private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull LoadWorldOptions options) {
462+
MultiverseWorld mvWorld = options.world();
463+
438464
World bukkitWorld = Bukkit.getWorld(mvWorld.getName());
439465
if (bukkitWorld != null) {
440-
if (bukkitWorld.getEnvironment() != mvWorld.getEnvironment()) {
441-
return Attempt.failure(LoadFailureReason.BUKKIT_ENVIRONMENT_MISMATCH,
442-
Replace.WORLD.with(mvWorld.getName()),
443-
replace("{bukkitEnvironment}").with(bukkitWorld.getEnvironment().name()),
444-
replace("{mvEnvironment}").with(mvWorld.getEnvironment().name()));
445-
}
446-
// World already loaded, maybe by another plugin
447-
Logging.finer("World already loaded in bukkit: " + mvWorld.getName());
448-
return newLoadedMultiverseWorld(mvWorld, bukkitWorld);
466+
return doLoadBukkitWorld(bukkitWorld, mvWorld);
467+
}
468+
469+
if (options.doFolderCheck() && !worldNameChecker.isValidWorldFolder(mvWorld.getName())) {
470+
return worldActionResult(LoadFailureReason.WORLD_FOLDER_INVALID, mvWorld.getName());
449471
}
450472

451473
WorldCreator worldCreator = WorldCreator.name(mvWorld.getName())
@@ -458,6 +480,18 @@ private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull M
458480
.mapAttempt(newBukkitWorld -> newLoadedMultiverseWorld(mvWorld, newBukkitWorld));
459481
}
460482

483+
private @NotNull Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadBukkitWorld(World bukkitWorld, MultiverseWorld mvWorld) {
484+
if (bukkitWorld.getEnvironment() != mvWorld.getEnvironment()) {
485+
return Attempt.failure(LoadFailureReason.BUKKIT_ENVIRONMENT_MISMATCH,
486+
Replace.WORLD.with(mvWorld.getName()),
487+
replace("{bukkitEnvironment}").with(bukkitWorld.getEnvironment().name()),
488+
replace("{mvEnvironment}").with(mvWorld.getEnvironment().name()));
489+
}
490+
// World already loaded, maybe by another plugin
491+
Logging.finer("World already loaded in bukkit: " + mvWorld.getName());
492+
return newLoadedMultiverseWorld(mvWorld, bukkitWorld);
493+
}
494+
461495
private Attempt<LoadedMultiverseWorld, LoadFailureReason> newLoadedMultiverseWorld(MultiverseWorld mvWorld, World bukkitWorld) {
462496
WorldConfig worldConfig = worldsConfigManager.getWorldConfig(mvWorld.getName()).get(); //TODO: null check here, but logically it should never be null.
463497
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
@@ -615,7 +649,7 @@ private Attempt<String, RemoveFailureReason> removeWorldFromConfig(@NotNull Mult
615649
*/
616650
public Attempt<String, DeleteFailureReason> deleteWorld(@NotNull DeleteWorldOptions options) {
617651
return getLoadedWorld(options.world()).fold(
618-
() -> loadWorld(options.world())
652+
() -> loadWorld(LoadWorldOptions.world(options.world()))
619653
.transform(DeleteFailureReason.LOAD_FAILED)
620654
.mapAttempt(world -> doDeleteWorld(world, options)),
621655
world -> doDeleteWorld(world, options));
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.mvplugins.multiverse.core.world.options;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
import org.mvplugins.multiverse.core.world.MultiverseWorld;
5+
6+
/**
7+
* Options for customizing the loading of a world.
8+
*
9+
* @since 5.2
10+
*/
11+
@ApiStatus.AvailableSince("5.2")
12+
public final class LoadWorldOptions {
13+
14+
/**
15+
* Creates a new {@link LoadWorldOptions} instance with the given world.
16+
*
17+
* @param world The world to load.
18+
* @return A new {@link LoadWorldOptions} instance.
19+
*
20+
* @since 5.2
21+
*/
22+
@ApiStatus.AvailableSince("5.2")
23+
public static LoadWorldOptions world(MultiverseWorld world) {
24+
return new LoadWorldOptions(world);
25+
}
26+
27+
private final MultiverseWorld world;
28+
private boolean doFolderCheck = true;
29+
30+
LoadWorldOptions(MultiverseWorld world) {
31+
this.world = world;
32+
}
33+
34+
/**
35+
* Gets the world to load.
36+
*
37+
* @return The world to load.
38+
*
39+
* @since 5.2
40+
*/
41+
@ApiStatus.AvailableSince("5.2")
42+
public MultiverseWorld world() {
43+
return world;
44+
}
45+
46+
/**
47+
* Sets whether to check if the world folder is valid before loading the world.
48+
* <br />
49+
* This helps to prevent deleted world folders from being re-created.
50+
*
51+
* @return Whether to check if the world folder is valid before loading the world.
52+
*
53+
* @since 5.2
54+
*/
55+
@ApiStatus.AvailableSince("5.2")
56+
public boolean doFolderCheck() {
57+
return doFolderCheck;
58+
}
59+
60+
/**
61+
* Sets whether to check if the world folder is valid before loading the world.
62+
*
63+
* @return This {@link LoadWorldOptions} instance.
64+
*
65+
* @since 5.2
66+
*/
67+
@ApiStatus.AvailableSince("5.2")
68+
public LoadWorldOptions doFolderCheck(boolean doFolderCheck) {
69+
this.doFolderCheck = doFolderCheck;
70+
return this;
71+
}
72+
}

src/main/java/org/mvplugins/multiverse/core/world/reasons/LoadFailureReason.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ public enum LoadFailureReason implements FailureReason {
2626
*/
2727
WORLD_EXIST_FOLDER(MVCorei18n.LOADWORLD_WORLDEXISTFOLDER),
2828

29+
/**
30+
* The world folder is invalid.
31+
*
32+
* @since 5.2
33+
*/
34+
@ApiStatus.AvailableSince("5.2")
35+
WORLD_FOLDER_INVALID(MVCorei18n.IMPORTWORLD_WORLDFOLDERINVALID),
36+
2937
/**
3038
* The world is already loaded.
3139
*/

src/test/java/org/mvplugins/multiverse/core/world/WorldManagerTest.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.mvplugins.multiverse.core.event.world.*
1010
import org.mvplugins.multiverse.core.world.options.CloneWorldOptions
1111
import org.mvplugins.multiverse.core.world.options.CreateWorldOptions
1212
import org.mvplugins.multiverse.core.world.options.DeleteWorldOptions
13+
import org.mvplugins.multiverse.core.world.options.LoadWorldOptions
1314
import org.mvplugins.multiverse.core.world.options.RegenWorldOptions
1415
import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions
1516
import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions
@@ -133,13 +134,23 @@ class WorldManagerTest : TestWithMockBukkit() {
133134
assertTrue(worldManager.getWorld("world2").isDefined)
134135
assertTrue(worldManager.getUnloadedWorld("world2").isDefined)
135136

136-
assertTrue(worldManager.loadWorld("world2").isSuccess)
137+
assertTrue(worldManager.loadWorld(LoadWorldOptions.world(world2)).isSuccess)
137138
assertTrue(world2.isLoaded)
138139
assertTrue(worldManager.getLoadedWorld("world2").flatMap{ w -> w.bukkitWorld }.isDefined)
139140
assertTrue(worldManager.getLoadedWorld("world2").isDefined)
140141
assertFalse(worldManager.getUnloadedWorld("world2").isDefined)
141142
}
142143

144+
@Test
145+
fun `Load world failed - invalid world folder`() {
146+
assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2)).isSuccess)
147+
File(Bukkit.getWorldContainer(), "world2/").deleteRecursively()
148+
assertEquals(
149+
LoadFailureReason.WORLD_FOLDER_INVALID,
150+
worldManager.loadWorld(LoadWorldOptions.world(world2)).failureReason
151+
)
152+
}
153+
143154
@Test
144155
fun `Load world failed - non-existent world`() {
145156
assertEquals(

0 commit comments

Comments
 (0)