From 2da9b0b0293d751ff178b6014e847ab3eb81ddb1 Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:08:09 +0800 Subject: [PATCH 1/3] Fix potential NPE with getFillMaterial if world is unloaded --- .../multiverse/portals/MVPortal.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java index 161e9fa..575c0be 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java +++ b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java @@ -468,7 +468,7 @@ private double getMinimumWith2Air(int finalX, int finalZ, int y, int yMax, World * this gets the Material at the center of the portal. * * @return The Material that fills this portal. - * @throws IllegalStateException If this portal's location is no longer valid. + * @throws IllegalStateException If this portal's location is no longer valid or world is unloaded. */ public Material getFillMaterial() throws IllegalStateException { if (!this.location.isValidLocation()) { @@ -476,13 +476,21 @@ public Material getFillMaterial() throws IllegalStateException { "Failed to get fill material from MV Portal (%s): Portal location is invalid.", this.getName())); } - + World world = this.location.getMultiverseWorld() + .flatMap(MultiverseWorld::asLoadedWorld) + .flatMap(LoadedMultiverseWorld::getBukkitWorld) + .getOrNull(); + if (world == null) { + String worldName = this.location.getMultiverseWorld() + .map(MultiverseWorld::getName) + .getOrElse("unknown"); + throw new IllegalStateException(String.format( + "Failed to get fill material from MV Portal (%s): World '%s' is unloaded.", + this.getName(), worldName)); + } return this.location.getMinimum() .getMidpoint(this.location.getMaximum()) - .toLocation(this.location.getMultiverseWorld() - .flatMap(MultiverseWorld::asLoadedWorld) - .flatMap(LoadedMultiverseWorld::getBukkitWorld) - .getOrNull()) + .toLocation(world) .getBlock() .getType(); } From 810a577563228209bb0a5b3cff7adda0cacdf6ac Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:09:54 +0800 Subject: [PATCH 2/3] Restore old constructors to ensure backwards compatibility --- .../multiverse/portals/MVPortal.java | 5 +++ .../multiverse/portals/PortalLocation.java | 10 ++++- .../portals/PortalPlayerSession.java | 9 +++-- .../portals/commands/CreateCommand.java | 3 +- .../portals/utils/MultiverseRegion.java | 37 +++++++++++++++++-- 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java index 575c0be..c151d47 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java +++ b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java @@ -95,12 +95,16 @@ public static MVPortal loadMVPortalFromConfig(MultiversePortals instance, String private Permission fillPermission; private Permission exempt; + /** + * @deprecated Use {@link MVPortal(MultiverseWorld, MultiversePortals, String, String, String)} instead. + */ @Deprecated(forRemoval = true, since = "5.3") @ApiStatus.ScheduledForRemoval(inVersion = "6.0") public MVPortal(LoadedMultiverseWorld world, MultiversePortals instance, String name, String owner, String location) { this((MultiverseWorld) world, instance, name, owner, location); } + @ApiStatus.AvailableSince("5.3") public MVPortal(MultiverseWorld world, MultiversePortals instance, String name, String owner, String location) { this(instance, name); this.setOwner(owner); @@ -328,6 +332,7 @@ public boolean setPortalLocation(String locationString, LoadedMultiverseWorld wo return this.setPortalLocation(locationString, (MultiverseWorld) world); } + @ApiStatus.AvailableSince("5.3") public boolean setPortalLocation(String locationString, MultiverseWorld world) { return this.setPortalLocation(PortalLocation.parseLocation(locationString, world, this.name)); } diff --git a/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java b/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java index 2a05054..5b11825 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java +++ b/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java @@ -57,6 +57,7 @@ public static PortalLocation parseLocation(String locationString, LoadedMultiver return parseLocation(locationString, (MultiverseWorld) world, portalName); } + @ApiStatus.AvailableSince("5.3") public static PortalLocation parseLocation(String locationString, MultiverseWorld world, String portalName) { String[] split = locationString.split(":"); if (split.length != 2) { @@ -88,6 +89,13 @@ private static PortalLocation getInvalidPortalLocation() { public PortalLocation() { } + @Deprecated(forRemoval = true, since = "5.3") + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public PortalLocation(Vector pos1, Vector pos2, LoadedMultiverseWorld world) { + this(pos1, pos2, (MultiverseWorld) world); + } + + @ApiStatus.AvailableSince("5.3") public PortalLocation(Vector pos1, Vector pos2, MultiverseWorld world) { this.validLocation = this.setLocation(pos1, pos2, world); } @@ -99,7 +107,7 @@ public PortalLocation(Vector pos1, Vector pos2, MultiverseWorld world) { * @param maxPt */ public PortalLocation(BlockVector3 minPt, BlockVector3 maxPt, LoadedMultiverseWorld world) { - this(new Vector(minPt.getX(), minPt.getY(), minPt.getZ()), new Vector(maxPt.getX(), maxPt.getY(), maxPt.getZ()), world); + this(new Vector(minPt.getX(), minPt.getY(), minPt.getZ()), new Vector(maxPt.getX(), maxPt.getY(), maxPt.getZ()), (MultiverseWorld) world); } private static Vector parseVector(String vectorString) { diff --git a/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java b/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java index 42bd181..e716859 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java +++ b/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java @@ -16,6 +16,7 @@ import org.mvplugins.multiverse.core.locale.message.Message; import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.enums.MoveType; @@ -153,7 +154,7 @@ public boolean setLeftClickSelection(Vector v, LoadedMultiverseWorld world) { this.leftClickWorld = world; String position = "(" + v.getBlockX() + ", " + v.getBlockY() + ", " + v.getBlockZ() + ")"; if (this.leftClickWorld == this.rightClickWorld && this.rightClick != null) { - MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); + MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, (MultiverseWorld) this.leftClickWorld); this.issuer.sendInfo(MVPi18n.SELECTION_FIRST_AREA, replace("{position}").with(position), Replace.COUNT.with(tempReg.getArea())); @@ -172,7 +173,7 @@ public boolean setRightClickSelection(Vector v, LoadedMultiverseWorld world) { this.rightClickWorld = world; String position = "(" + v.getBlockX() + ", " + v.getBlockY() + ", " + v.getBlockZ() + ")"; if (this.leftClickWorld == this.rightClickWorld && this.leftClick != null) { - MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); + MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, (MultiverseWorld) this.leftClickWorld); this.issuer.sendInfo(MVPi18n.SELECTION_SECOND_AREA, replace("{position}").with(position), Replace.COUNT.with(tempReg.getArea())); @@ -192,7 +193,7 @@ public MultiverseRegion getSelectedRegion() { Location maxPoint = worldEdit.getSelectionMaxPoint(this.player); if (minPoint != null && maxPoint != null && minPoint.getWorld().equals(maxPoint.getWorld())) { return new MultiverseRegion(minPoint, maxPoint, - this.worldManager.getLoadedWorld(minPoint.getWorld().getName()).getOrNull()); + this.worldManager.getWorld(minPoint.getWorld().getName()).getOrNull()); } else { this.issuer.sendError(MVPi18n.SELECTION_WORLDEDIT_INCOMPLETE); return null; @@ -217,7 +218,7 @@ public MultiverseRegion getSelectedRegion() { replace("{rightWorld}").with(this.rightClickWorld.getAlias())); return null; } - return new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); + return new MultiverseRegion(this.leftClick, this.rightClick, (MultiverseWorld) this.leftClickWorld); } /** diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java index 3f77052..7ceab38 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java @@ -6,6 +6,7 @@ import org.mvplugins.multiverse.core.destination.DestinationInstance; import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; @@ -76,7 +77,7 @@ void onCreateCommand( } MVPortal portal = this.portalManager.getPortal(portalName); - PortalLocation location = new PortalLocation(region.getMinimumPoint(), region.getMaximumPoint(), world); + PortalLocation location = new PortalLocation(region.getMinimumPoint(), region.getMaximumPoint(), (MultiverseWorld) world); if (this.portalManager.addPortal(world, portalName, player.getName(), location)) { issuer.sendInfo(MVPi18n.CREATE_SUCCESS, Replace.NAME.with(portalName)); // If the portal did not exist, ie: we're creating it. diff --git a/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java b/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java index 5cfddfd..4d3690a 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java +++ b/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java @@ -26,7 +26,17 @@ public class MultiverseRegion { private final Vector max; private final MultiverseWorld world; - public MultiverseRegion(Object pos1, Object pos2, MultiverseWorld w) { + /** + * @deprecated Use {@link MultiverseRegion(Object, Object, MultiverseWorld)} instead. + */ + @Deprecated(forRemoval = true, since = "5.3") + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public MultiverseRegion(Object pos1, Object pos2, LoadedMultiverseWorld w) { + this(pos1, pos2, (MultiverseWorld) w); + } + + @ApiStatus.AvailableSince("5.3") + public MultiverseRegion(Object pos1, Object pos2, MultiverseWorld w) { // Creating soft dependencies on WE if (pos1 instanceof com.sk89q.worldedit.math.BlockVector3 && pos2 instanceof com.sk89q.worldedit.math.BlockVector3) { com.sk89q.worldedit.math.BlockVector3 weV1 = (com.sk89q.worldedit.math.BlockVector3) pos1; @@ -36,15 +46,36 @@ public MultiverseRegion(Object pos1, Object pos2, MultiverseWorld w) { this.min = Vector.getMinimum(tmp1, tmp2); this.max = Vector.getMaximum(tmp1, tmp2); this.world = w; + return; } throw new UnsupportedOperationException("WorldEdit plugin not installed!"); } - public MultiverseRegion(Location loc1, Location loc2, MultiverseWorld w) { + /** + * @deprecated Use {@link MultiverseRegion(Location, Location, MultiverseWorld)} instead. + */ + @Deprecated(forRemoval = true, since = "5.3") + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public MultiverseRegion(Location loc1, Location loc2, LoadedMultiverseWorld w) { + this(loc1.toVector(), loc2.toVector(), (MultiverseWorld) w); + } + + @ApiStatus.AvailableSince("5.3") + public MultiverseRegion(Location loc1, Location loc2, MultiverseWorld w) { this(loc1.toVector(), loc2.toVector(), w); } - public MultiverseRegion(Vector pos1, Vector pos2, MultiverseWorld w) { + /** + * @deprecated Use {@link MultiverseRegion(Vector, Vector, MultiverseWorld)} instead. + */ + @Deprecated(forRemoval = true, since = "5.3") + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public MultiverseRegion(Vector pos1, Vector pos2, LoadedMultiverseWorld w) { + this(pos1, pos2, (MultiverseWorld) w); + } + + @ApiStatus.AvailableSince("5.3") + public MultiverseRegion(Vector pos1, Vector pos2, MultiverseWorld w) { this.min = Vector.getMinimum(pos1, pos2); this.max = Vector.getMaximum(pos1, pos2); this.world = w; From 215e91d2f2a4f4916018b9b66bfb39c0d86fb6da Mon Sep 17 00:00:00 2001 From: Ben Woo <30431861+benwoo1110@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:10:38 +0800 Subject: [PATCH 3/3] Fix getPortals world matching check --- .../org/mvplugins/multiverse/portals/utils/PortalManager.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java b/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java index f8b3fee..d25f13f 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java +++ b/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java @@ -13,7 +13,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import com.dumptruckman.minecraft.util.Logging; import org.bukkit.Location; @@ -38,7 +37,6 @@ import org.mvplugins.multiverse.portals.PortalLocation; import org.mvplugins.multiverse.portals.config.PortalsConfig; - /** * Manages all portals for all worlds. * @@ -278,7 +276,7 @@ public List getPortals(CommandSender sender, MultiverseWorld world) { if (portalsConfig.getEnforcePortalAccess()) { for (MVPortal p : all) { if (p.getPortalLocation().isValidLocation() - && Objects.equals(p.getPortalLocation().getMultiverseWorld(), world) + && p.getPortalLocation().getMultiverseWorld().exists(world::equals) && p.playerCanEnterPortal((Player) sender)) { validItems.add(p); }