Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
Expand All @@ -22,6 +22,11 @@

/**
* Extension of {@link MultiverseWorld} that represents a world that is currently loaded with bukkit world object.
* <p>

Check warning on line 25 in src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be placed immediately before the first word, with no space after. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java:25:0: warning: <p> tag should be placed immediately before the first word, with no space after. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)
* Do not store instances of this class for future use during runtime. Worlds can be unloaded, removed, reloaded,
* or regenerated, making stored instances stale. Instead, store a {@link MultiverseWorldRef} obtained from
* {@link #asRef()} and call {@link MultiverseWorldRef#getLoaded()} when a loaded world is needed. The reference
* returns an empty Option when the world is unloaded or no longer managed by Multiverse.
*/
public final class LoadedMultiverseWorld extends MultiverseWorld {

Expand All @@ -34,16 +39,13 @@
LoadedMultiverseWorld(
@NotNull World world,
@NotNull WorldConfig worldConfig,
@NotNull CoreConfig config,
@NotNull BlockSafety blockSafety,
@NotNull LocationManipulation locationManipulation,
@NotNull EntityPurger entityPurger
) {
super(worldConfig, config);
@NotNull MultiverseCore multiverseCore
) {
super(worldConfig, multiverseCore);
this.worldUid = world.getUID();
this.blockSafety = blockSafety;
this.locationManipulation = locationManipulation;
this.entityPurger = entityPurger;
this.blockSafety = multiverseCore.getServiceLocator().getService(BlockSafety.class);
this.locationManipulation = multiverseCore.getServiceLocator().getService(LocationManipulation.class);
this.entityPurger = multiverseCore.getServiceLocator().getService(EntityPurger.class);

setupWorldConfig(world);
setupSpawnLocation(world);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.jetbrains.annotations.Nullable;

import org.jetbrains.annotations.UnmodifiableView;
import org.mvplugins.multiverse.core.MultiverseCore;

Check warning on line 23 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 'org.mvplugins.multiverse.core.MultiverseCore' should be separated from previous imports. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java:23:1: warning: 'org.mvplugins.multiverse.core.MultiverseCore' should be separated from previous imports. (com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck)
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
Expand All @@ -29,6 +30,11 @@

/**
* Represents a world handled by Multiverse which has all the custom properties provided by Multiverse.
* <p>

Check warning on line 33 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be preceded with an empty line. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java:33:0: warning: <p> tag should be preceded with an empty line. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)

Check warning on line 33 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be placed immediately before the first word, with no space after. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java:33:0: warning: <p> tag should be placed immediately before the first word, with no space after. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)
* Do not store instances of this class for future use during runtime. Worlds can be unloaded, removed, reloaded,
* or regenerated, making stored instances stale. Instead, store a {@link MultiverseWorldRef} obtained from
* {@link #asRef()} and retrieve the current world instance when needed. The reference returns an empty Option
* when the world is no longer available.
*/
public sealed class MultiverseWorld permits LoadedMultiverseWorld {
/**
Expand All @@ -37,11 +43,13 @@
WorldConfig worldConfig;

protected final CoreConfig config;
private final WorldStore worldStore;
private String colourlessAlias = "";

MultiverseWorld(WorldConfig worldConfig, CoreConfig config) {
MultiverseWorld(@NotNull WorldConfig worldConfig, @NotNull MultiverseCore multiverseCore) {
this.worldConfig = worldConfig;
this.config = config;
this.config = multiverseCore.getServiceLocator().getService(CoreConfig.class);
this.worldStore = multiverseCore.getServiceLocator().getService(WorldStore.class);
this.worldConfig.setMVWorld(this);
updateColourlessAlias();
}
Expand Down Expand Up @@ -125,6 +133,22 @@
.map(world -> (LoadedMultiverseWorld) world);
}

/**
* Creates a reference to this world's key for storing and retrieving the current world instance at runtime.
* <p>

Check warning on line 138 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be preceded with an empty line. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java:138:0: warning: <p> tag should be preceded with an empty line. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)

Check warning on line 138 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be placed immediately before the first word, with no space after. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java:138:0: warning: <p> tag should be placed immediately before the first word, with no space after. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)
* Store this reference when retaining a world across reloads or regeneration, which make stored
* {@link MultiverseWorld} and {@link LoadedMultiverseWorld} instances stale. Use {@link MultiverseWorldRef#get()}
* or {@link MultiverseWorldRef#getLoaded()} to retrieve the current instance when needed.
*
* @return A reference that looks up the current world instance using this world's key.
*
* @since 5.9
*/
@ApiStatus.AvailableSince("5.9")
public MultiverseWorldRef asRef() {
return new MultiverseWorldRef(getKey(), worldStore);
}

/**
* Gets the properties handler of this world.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.mvplugins.multiverse.core.world;

import io.vavr.control.Option;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

/**
* A reference to a world by its key that can be stored and used to retrieve the current world instance at runtime.
* <p>

Check warning on line 10 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be preceded with an empty line. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java:10:0: warning: <p> tag should be preceded with an empty line. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)

Check warning on line 10 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be placed immediately before the first word, with no space after. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java:10:0: warning: <p> tag should be placed immediately before the first word, with no space after. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)
* Stored {@link MultiverseWorld} and {@link LoadedMultiverseWorld} instances become stale when a world is reloaded
* or regenerated. This reference looks up the world on each call to {@link #get()} or {@link #getLoaded()}, allowing
* callers to keep the reference across those operations and retrieve the current instance when needed.
* Store this reference rather than caching the world instances returned by its methods.
* <p>

Check warning on line 15 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be preceded with an empty line. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java:15:0: warning: <p> tag should be preceded with an empty line. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)

Check warning on line 15 in src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 <p> tag should be placed immediately before the first word, with no space after. Raw Output: /github/workspace/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorldRef.java:15:0: warning: <p> tag should be placed immediately before the first word, with no space after. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck)
* Obtain a reference using {@link MultiverseWorld#asRef()}. Resolving a reference does not load the world.
*
* @since 5.9
*/
@ApiStatus.AvailableSince("5.9")
public class MultiverseWorldRef {

private final NamespacedKey worldKey;
private final WorldStore worldStore;

MultiverseWorldRef(@NotNull NamespacedKey worldKey, @NotNull WorldStore worldStore) {
this.worldKey = worldKey;
this.worldStore = worldStore;
}

/**
* Gets the current world instance for this reference's key, whether loaded or unloaded.
* The world is looked up on each call so the returned instance reflects reloads and regeneration.
*
* @return The current world instance wrapped in an Option, or None if the world is no longer managed by Multiverse.
*
* @since 5.9
*/
@ApiStatus.AvailableSince("5.9")
public Option<MultiverseWorld> get() {
return worldStore.getWorld(worldKey);
}

/**
* Gets the current loaded world instance for this reference's key without loading the world.
* The world is looked up on each call so the returned instance reflects reloads and regeneration.
*
* @return The current loaded world instance wrapped in an Option, or None if the world is unloaded or no longer
* managed by Multiverse.
*
* @since 5.9
*/
@ApiStatus.AvailableSince("5.9")
public Option<LoadedMultiverseWorld> getLoaded() {
return worldStore.getLoadedWorld(worldKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ final class WorldConfigNodes {

private final NodeGroup nodes = new NodeGroup();
private WorldManager worldManager;
private WorldStore worldStore;
private EnforcementHandler enforcementHandler;
private CoreConfig config;
private AliasNameConflictChecker aliasNameConflictChecker;
Expand All @@ -52,6 +53,7 @@ final class WorldConfigNodes {

WorldConfigNodes(@NotNull MultiverseCore multiverseCore, @NotNull WorldKeyOrName keyOrName) {
this.worldManager = multiverseCore.getServiceLocator().getService(WorldManager.class);
this.worldStore = multiverseCore.getServiceLocator().getService(WorldStore.class);
this.enforcementHandler = multiverseCore.getServiceLocator().getService(EnforcementHandler.class);
this.config = multiverseCore.getServiceLocator().getService(CoreConfig.class);
this.aliasNameConflictChecker = multiverseCore.getServiceLocator().getService(AliasNameConflictChecker.class);
Expand Down Expand Up @@ -100,7 +102,7 @@ private <T> ConfigNode<T> node(ConfigNode.Builder<T, ?> nodeBuilder) {
final ConfigNode<String> alias = node(ConfigNode.builder("alias", String.class)
.defaultValue("")
.onLoadAndChange((sender, oldValue, newValue) -> {
worldManager.getWorldStore().changeAlias(
worldStore.changeAlias(
ChatTextFormatter.removeColor(oldValue),
ChatTextFormatter.removeColor(newValue),
keyOrName.usableKey()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.jetbrains.annotations.Unmodifiable;
import org.jvnet.hk2.annotations.Service;

import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.config.CoreConfig;
import org.mvplugins.multiverse.core.event.world.MVWorldClonedEvent;
import org.mvplugins.multiverse.core.event.world.MVWorldCreatedEvent;
Expand All @@ -45,8 +46,6 @@
import org.mvplugins.multiverse.core.locale.message.Message;
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
import org.mvplugins.multiverse.core.permissions.CorePermissions;
import org.mvplugins.multiverse.core.teleportation.BlockSafety;
import org.mvplugins.multiverse.core.teleportation.LocationManipulation;
import org.mvplugins.multiverse.core.utils.ServerProperties;
import org.mvplugins.multiverse.core.utils.compatibility.BukkitCompatibility;
import org.mvplugins.multiverse.core.utils.compatibility.WorldCompatibility;
Expand All @@ -56,7 +55,6 @@
import org.mvplugins.multiverse.core.utils.FileUtils;
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
import org.mvplugins.multiverse.core.world.biomeprovider.BiomeProviderFactory;
import org.mvplugins.multiverse.core.world.entity.EntityPurger;
import org.mvplugins.multiverse.core.world.generators.GeneratorProvider;
import org.mvplugins.multiverse.core.world.helpers.DataStore.GameRulesStore;
import org.mvplugins.multiverse.core.world.helpers.DataTransfer;
Expand Down Expand Up @@ -96,6 +94,7 @@ public final class WorldManager {
"data/paper/metadata.dat" // New papermc format for 26.1+
);

private final MultiverseCore multiverseCore;
private final WorldStore worldStore;
private final List<String> unloadTracker;
private final List<String> loadTracker;
Expand All @@ -104,44 +103,37 @@ public final class WorldManager {
private final BiomeProviderFactory biomeProviderFactory;
private final GeneratorProvider generatorProvider;
private final FileUtils fileUtils;
private final BlockSafety blockSafety;
private final LocationManipulation locationManipulation;
private final PluginManager pluginManager;
private final CorePermissions corePermissions;
private final ServerProperties serverProperties;
private final CoreConfig config;
private final EntityPurger entityPurger;
private final Provider<PotentialWorldFinder> potentialWorldFinder;

@Inject
WorldManager(
@NotNull MultiverseCore multiverseCore,
@NotNull WorldStore worldStore,
@NotNull WorldsConfigManager worldsConfigManager,
@NotNull WorldNameChecker worldNameChecker,
@NotNull BiomeProviderFactory biomeProviderFactory,
@NotNull GeneratorProvider generatorProvider,
@NotNull FileUtils fileUtils,
@NotNull BlockSafety blockSafety,
@NotNull LocationManipulation locationManipulation,
@NotNull PluginManager pluginManager,
@NotNull CorePermissions corePermissions,
@NotNull ServerProperties serverProperties,
@NotNull CoreConfig config,
@NotNull EntityPurger entityPurger,
@NotNull Provider<PotentialWorldFinder> potentialWorldFinder) {
this.multiverseCore = multiverseCore;
this.worldStore = worldStore;
this.worldsConfigManager = worldsConfigManager;
this.worldNameChecker = worldNameChecker;
this.biomeProviderFactory = biomeProviderFactory;
this.generatorProvider = generatorProvider;
this.fileUtils = fileUtils;
this.blockSafety = blockSafety;
this.locationManipulation = locationManipulation;
this.pluginManager = pluginManager;
this.corePermissions = corePermissions;
this.serverProperties = serverProperties;
this.config = config;
this.entityPurger = entityPurger;
this.potentialWorldFinder = potentialWorldFinder;

this.unloadTracker = new ArrayList<>();
Expand Down Expand Up @@ -417,7 +409,7 @@ private Attempt<LoadedMultiverseWorld, ImportFailureReason> doImportBukkitWorld(
}

private MultiverseWorld newMultiverseWorld(WorldConfig worldConfig) {
MultiverseWorld mvWorld = new MultiverseWorld(worldConfig, config);
MultiverseWorld mvWorld = new MultiverseWorld(worldConfig, multiverseCore);
worldStore.putUnloadedWorld(mvWorld);
corePermissions.addWorldPermissions(mvWorld);
return mvWorld;
Expand Down Expand Up @@ -456,10 +448,7 @@ private LoadedMultiverseWorld newLoadedMultiverseWorld(
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
world,
worldConfig,
config,
blockSafety,
locationManipulation,
entityPurger
multiverseCore
);
worldStore.putLoadedWorld(loadedWorld);
saveWorldsConfig();
Expand Down Expand Up @@ -592,10 +581,7 @@ private Attempt<LoadedMultiverseWorld, LoadFailureReason> newLoadedMultiverseWor
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
bukkitWorld,
worldConfig,
config,
blockSafety,
locationManipulation,
entityPurger
multiverseCore
);
worldStore.putLoadedWorld(loadedWorld);
saveWorldsConfig();
Expand Down Expand Up @@ -1278,10 +1264,6 @@ public Try<Void> saveWorldsConfig() {
});
}

WorldStore getWorldStore() {
return worldStore;
}

/**
* A simple pair wrapper for convenience to pass both the world key or name and the options together between methods
* when parsing world options.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,48 @@ class WorldManagerTest : TestWithMockBukkit() {
assertNotNull(world2)
}

@Test
fun `World reference follows unload and reload`() {
val ref = world2.asRef()
assertSame(world2, ref.get().get())
assertSame(world2, ref.getLoaded().get())

assertTrue(worldManager.unloadWorld(UnloadWorldOptions.world(world2)).isSuccess)
assertTrue(ref.getLoaded().isEmpty)
val unloaded = ref.get().get()
assertFalse(unloaded.isLoaded)
assertSame(worldManager.getWorld(world2.name).get(), unloaded)

val unloadedRef = unloaded.asRef()
assertTrue(worldManager.loadWorld(LoadWorldOptions.world(unloaded)).isSuccess)
val reloaded = worldManager.getLoadedWorld(world2.name).get()
assertNotSame(world2, reloaded)
assertSame(reloaded, ref.get().get())
assertSame(reloaded, ref.getLoaded().get())
assertSame(reloaded, unloadedRef.getLoaded().get())
}

@Test
fun `World reference follows regeneration`() {
val ref = world2.asRef()
assertTrue(worldManager.regenWorld(RegenWorldOptions.world(world2).seed(4321L)).isSuccess)

val regenerated = ref.getLoaded().get()
assertNotSame(world2, regenerated)
assertSame(worldManager.getLoadedWorld(world2.name).get(), regenerated)
assertSame(regenerated, ref.get().get())
assertEquals(4321L, regenerated.seed)
}

@Test
fun `World reference is empty after removal`() {
val ref = world2.asRef()
assertTrue(worldManager.removeWorld(RemoveWorldOptions.world(world2)).isSuccess)

assertTrue(ref.get().isEmpty)
assertTrue(ref.getLoaded().isEmpty)
}

@Test
fun `Create world with custom options`() {
assertTrue(worldManager.createWorld(
Expand Down
Loading