World Changes

This commit is contained in:
Brian Neumann-Fopiano
2026-07-12 17:06:30 -04:00
parent e42ab6fae6
commit cd05fd1bca
58 changed files with 668 additions and 313 deletions
@@ -23,6 +23,7 @@ import art.arcane.iris.engine.IrisWorldManager;
import art.arcane.iris.engine.framework.EngineWorldManagerProvider;
import art.arcane.iris.core.splash.IrisSplashComposer;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.IrisWorldStorage;
import art.arcane.iris.core.IrisWorlds;
import art.arcane.iris.core.ServerConfigurator;
import art.arcane.iris.core.datapack.DatapackIngestService;
@@ -61,6 +62,7 @@ import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.spi.LogLevel;
import art.arcane.volmlib.integration.ReloadAware;
import art.arcane.volmlib.util.bukkit.WorldIdentity;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.exceptions.IrisException;
@@ -88,6 +90,7 @@ import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.block.data.BlockData;
@@ -590,6 +593,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
}
private void enable() {
alreadyDrained.set(false);
PaperLibBootstrap.install();
SimdSupport.install();
services = new KMap<>();
@@ -646,7 +650,6 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
J.ar(this::checkConfigHotload, 60);
J.sr(this::tickQueue, 0);
J.s(this::setupPapi);
J.a(ServerConfigurator::configureIfDeferred, 20);
J.a(DatapackIngestService::autoIngestOnStartup, 60);
autoStartStudio();
@@ -668,17 +671,22 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
}
}
shutdownHook = new Thread(() -> {
Bukkit.getWorlds()
.stream()
.map(IrisToolbelt::access)
.filter(Objects::nonNull)
.forEach(PlatformChunkGenerator::close);
if (alreadyDrained.compareAndSet(false, true)) {
try {
Bukkit.getWorlds()
.stream()
.map(World::getGenerator)
.filter(PlatformChunkGenerator.class::isInstance)
.map(PlatformChunkGenerator.class::cast)
.forEach(PlatformChunkGenerator::close);
} catch (Throwable e) {
Iris.reportError("Failed to close Iris world generators from the JVM shutdown hook.", e);
}
}
MultiBurst.burst.close();
MultiBurst.ioBurst.close();
if (services != null) {
services.clear();
}
IrisServices.clear();
}, "Iris-ShutdownHook");
try {
Runtime.getRuntime().addShutdownHook(shutdownHook);
@@ -692,15 +700,16 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
KList<String> deferredStartupWorlds = new KList<>();
IrisWorlds.readBukkitWorlds().forEach((s, generator) -> {
try {
if (Bukkit.getWorld(s) != null || !filter.test(s)) return;
NamespacedKey worldKey = IrisWorldStorage.keyFromLegacyName(s);
if (WorldIdentity.resolve(worldKey).isPresent() || !filter.test(s)) return;
Iris.info("Loading World: %s | Generator: %s", s, generator);
var gen = getDefaultWorldGenerator(s, generator);
var dim = loadDimension(s, generator);
ChunkGenerator gen = getDefaultWorldGenerator(s, generator);
IrisDimension dim = loadDimension(s, generator);
assert dim != null && gen != null;
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
WorldCreator c = new WorldCreator(s)
WorldCreator c = WorldCreator.ofKey(worldKey)
.generator(gen)
.environment(dim.getEnvironment());
Long stagedSeed = IrisWorlds.readBukkitWorldSeed(s);
@@ -786,7 +795,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
}
LinkedHashMap<String, String> queue = loadPendingWorldDeleteMap();
for (String transientStudioWorld : TransientWorldCleanupSupport.collectTransientStudioWorldNames(Bukkit.getWorldContainer())) {
for (String transientStudioWorld : TransientWorldCleanupSupport.collectTransientStudioWorldNames(IrisWorldStorage.levelRoot())) {
queue.putIfAbsent(transientStudioWorld.toLowerCase(Locale.ROOT), transientStudioWorld);
}
if (queue.isEmpty()) {
@@ -800,7 +809,8 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
continue;
}
org.bukkit.World loaded = Bukkit.getWorld(worldName);
NamespacedKey worldKey = IrisWorldStorage.keyFromLegacyName(worldName);
World loaded = WorldIdentity.resolve(worldKey).orElse(null);
if (loaded != null) {
if (TransientWorldCleanupSupport.isTransientStudioWorldName(worldName)) {
try {
@@ -815,7 +825,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
Iris.reportError("Failed to unload leftover studio world \"" + worldName + "\".", e);
}
if (Bukkit.getWorld(worldName) != null) {
if (WorldIdentity.resolve(worldKey).isPresent()) {
Iris.warn("Studio world \"" + worldName + "\" is still loaded after unload; will retry next startup.");
remaining.put(worldName.toLowerCase(Locale.ROOT), worldName);
continue;
@@ -830,7 +840,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
boolean foundAny = false;
boolean deletedAll = true;
for (String familyWorldName : TransientWorldCleanupSupport.worldFamilyNames(worldName)) {
File worldFolder = new File(Bukkit.getWorldContainer(), familyWorldName);
File worldFolder = IrisWorldStorage.dimensionRoot(familyWorldName);
if (!worldFolder.exists()) {
continue;
}
@@ -975,7 +985,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
public void onDisable() {
if (IrisSafeguard.isForceShutdown()) return;
if (!alreadyDrained.get()) {
if (alreadyDrained.compareAndSet(false, true)) {
drainWorldGenerators("onDisable", 30L);
}
if (services != null) {
@@ -1202,12 +1212,14 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
}
Iris.debug("Assuming IrisDimension: " + dim.getName());
NamespacedKey worldKey = IrisWorldStorage.keyFromLegacyName(worldName);
IrisWorld w = IrisWorld.builder()
.key(worldKey)
.name(worldName)
.seed(1337)
.environment(dim.getEnvironment())
.worldFolder(new File(Bukkit.getWorldContainer(), worldName))
.worldFolder(IrisWorldStorage.dimensionRoot(worldKey))
.minHeight(dim.getMinHeight())
.maxHeight(dim.getMaxHeight())
.build();
@@ -1215,7 +1227,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
Iris.debug("Generator Config: " + w.toString());
File ff = new File(w.worldFolder(), "iris/pack");
var files = ff.listFiles();
File[] files = ff.listFiles();
if (files == null || files.length == 0)
IO.delete(ff);
@@ -1263,8 +1275,8 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
@Nullable
public static IrisDimension loadDimension(@NonNull String worldName, @NonNull String id) {
File pack = new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pack"));
var dimension = pack.isDirectory() ? IrisData.get(pack).getDimensionLoader().load(id) : null;
File pack = IrisWorldStorage.packRoot(IrisWorldStorage.keyFromLegacyName(worldName));
IrisDimension dimension = pack.isDirectory() ? IrisData.get(pack).getDimensionLoader().load(id) : null;
if (dimension == null) dimension = IrisData.loadAnyDimension(id, null);
if (dimension == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
@@ -31,15 +31,19 @@ import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.iris.util.common.director.specialhandlers.ObjectHandler;
import art.arcane.iris.util.common.director.specialhandlers.StructureHandler;
import art.arcane.iris.util.common.format.C;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.common.scheduling.J;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.director.DirectorOrigin;
import art.arcane.volmlib.util.director.annotations.Director;
import art.arcane.volmlib.util.director.annotations.Param;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.entity.Player;
import org.bukkit.generator.structure.Structure;
import org.bukkit.util.StructureSearchResult;
@Director(name = "find", origin = DirectorOrigin.PLAYER, description = "Iris Find commands", aliases = "goto")
@@ -99,10 +103,16 @@ public class CommandFind implements DirectorExecutor {
@Param(description = "The structure to look for (e.g. minecraft:village_plains, minecraft:stronghold, minecraft_ancient_city)", customHandler = StructureHandler.class)
String structure
) {
VolmitSender commandSender = sender();
if (commandSender == null) {
Iris.reportError("Structure lookup started without a command sender context.", new IllegalStateException("Missing command sender context"));
return;
}
Engine e = engine();
if (e == null) {
sender().sendMessage(C.GOLD + "Not in an Iris World!");
commandSender.sendMessage(C.GOLD + "Not in an Iris World!");
return;
}
@@ -113,43 +123,45 @@ public class CommandFind implements DirectorExecutor {
Player target = player();
if (target == null) {
sender().sendMessage(C.GOLD + "Run this in-game to teleport to a structure.");
commandSender.sendMessage(C.GOLD + "Run this in-game to teleport to a structure.");
return;
}
sender().sendMessage(C.GRAY + "Locating " + structure + "...");
commandSender.sendMessage(C.GRAY + "Locating " + structure + "...");
J.s(() -> {
try {
org.bukkit.generator.structure.Structure match = null;
for (org.bukkit.generator.structure.Structure s : Registry.STRUCTURE) {
NamespacedKey key = s.getKey();
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
Structure match = null;
for (Structure candidate : structureRegistry) {
NamespacedKey key = structureRegistry.getKey(candidate);
if (key != null && key.toString().equalsIgnoreCase(structure)) {
match = s;
match = candidate;
break;
}
}
if (match == null) {
sender().sendMessage(C.RED + "Unknown structure: " + structure);
commandSender.sendMessage(C.RED + "Unknown structure: " + structure);
return;
}
if (!StructureReachability.isReachable(e, structure)) {
KList<String> miss = StructureReachability.missingBiomeKeys(e, structure);
sender().sendMessage(C.YELLOW + structure + " cannot generate in this world (its required biomes are not produced by this pack"
commandSender.sendMessage(C.YELLOW + structure + " cannot generate in this world (its required biomes are not produced by this pack"
+ (miss.isEmpty() ? "" : ": needs " + String.join("/", miss)) + ").");
return;
}
StructureSearchResult result = target.getWorld().locateNearestStructure(target.getLocation(), match, 100, true);
if (result == null || result.getLocation() == null) {
sender().sendMessage(C.YELLOW + "No " + structure + " found within range of you.");
commandSender.sendMessage(C.YELLOW + "No " + structure + " found within range of you.");
return;
}
Location at = result.getLocation();
int y = target.getWorld().getHighestBlockYAt(at.getBlockX(), at.getBlockZ()) + 2;
Location dest = new Location(target.getWorld(), at.getBlockX() + 0.5, y, at.getBlockZ() + 0.5);
BukkitPlatform.teleportAsync(target, dest);
sender().sendMessage(C.GREEN + "Teleported to " + structure + " @ " + at.getBlockX() + ", " + at.getBlockZ());
commandSender.sendMessage(C.GREEN + "Teleported to " + structure + " @ " + at.getBlockX() + ", " + at.getBlockZ());
} catch (Throwable t) {
sender().sendMessage(C.RED + "Could not locate " + structure + ": " + t.getClass().getSimpleName());
commandSender.sendMessage(C.RED + "Could not locate " + structure + ": " + t.getClass().getSimpleName());
Iris.reportError("Could not locate structure '" + structure + "'.", t);
}
});
}
@@ -21,6 +21,7 @@ package art.arcane.iris.core.commands;
import art.arcane.iris.Iris;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.IrisWorldStorage;
import art.arcane.iris.core.IrisWorlds;
import art.arcane.iris.core.ServerConfigurator;
import art.arcane.iris.core.lifecycle.WorldLifecycleService;
@@ -30,6 +31,7 @@ import art.arcane.iris.core.tools.IrisToolbelt;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.bukkit.WorldIdentity;
import art.arcane.volmlib.util.director.DirectorParameterHandler;
import art.arcane.iris.util.common.director.DirectorExecutor;
import art.arcane.volmlib.util.director.DirectorOrigin;
@@ -44,6 +46,7 @@ import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.common.scheduling.J;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -55,6 +58,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@@ -108,7 +112,7 @@ public class CommandIris implements DirectorExecutor {
return;
}
if (new File(Bukkit.getWorldContainer(), name).exists()) {
if (IrisWorldStorage.dimensionRoot(name).exists()) {
sender().sendMessage(C.RED + "That folder already exists!");
return;
}
@@ -161,29 +165,44 @@ public class CommandIris implements DirectorExecutor {
private boolean updateMainWorld(String newName) {
try {
File worlds = Bukkit.getWorldContainer();
var data = ServerProperties.DATA;
try (var in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
File oldLevelRoot = IrisWorldStorage.levelRoot();
File worldContainer = oldLevelRoot.getParentFile();
Properties data = ServerProperties.DATA;
try (FileInputStream in = new FileInputStream(ServerProperties.SERVER_PROPERTIES)) {
data.load(in);
}
File oldWorldFolder = new File(worlds, ServerProperties.LEVEL_NAME);
File newWorldFolder = new File(worlds, newName);
if (!newWorldFolder.exists() && !newWorldFolder.mkdirs()) {
Iris.warn("Could not create target main world folder: " + newWorldFolder.getAbsolutePath());
File sourceDimensionRoot = IrisWorldStorage.dimensionRoot(IrisWorldStorage.keyFromLegacyName(newName));
if (!sourceDimensionRoot.isDirectory()) {
throw new IllegalStateException("Source dimension folder does not exist: " + sourceDimensionRoot.getAbsolutePath());
}
for (String sub : List.of("datapacks", "playerdata", "advancements", "stats")) {
File source = new File(oldWorldFolder, sub);
File newLevelRoot = new File(worldContainer, newName);
if (!newLevelRoot.exists() && !newLevelRoot.mkdirs()) {
throw new IllegalStateException("Could not create target level folder: " + newLevelRoot.getAbsolutePath());
}
for (String sub : List.of("data", "datapacks", "players")) {
File source = new File(oldLevelRoot, sub);
if (!source.exists()) {
continue;
}
IO.copyDirectory(source.toPath(), new File(newWorldFolder, sub).toPath());
IO.copyDirectory(source.toPath(), new File(newLevelRoot, sub).toPath());
}
File targetDimensionRoot = IrisWorldStorage.dimensionRoot(newLevelRoot, NamespacedKey.minecraft("overworld"));
IO.copyDirectory(sourceDimensionRoot.toPath(), targetDimensionRoot.toPath());
World sourceWorld = WorldIdentity.resolve(IrisWorldStorage.keyFromLegacyName(newName)).orElse(null);
Long stagedSeed = IrisWorlds.readBukkitWorldSeed(newName);
if (sourceWorld == null && stagedSeed == null) {
throw new IllegalStateException("Cannot determine the promoted world's seed.");
}
long promotedSeed = sourceWorld == null ? stagedSeed : sourceWorld.getSeed();
data.setProperty("level-name", newName);
try (var out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
data.setProperty("level-seed", Long.toString(promotedSeed));
try (FileOutputStream out = new FileOutputStream(ServerProperties.SERVER_PROPERTIES)) {
data.store(out, null);
}
return true;
@@ -198,7 +217,7 @@ public class CommandIris implements DirectorExecutor {
sender().sendMessage(C.YELLOW + "Runtime world creation is disabled on Folia.");
sender().sendMessage(C.YELLOW + "Preparing world files and bukkit.yml for next startup...");
File worldFolder = new File(Bukkit.getWorldContainer(), name);
File worldFolder = IrisWorldStorage.dimensionRoot(name);
IrisDimension installed = Iris.service(StudioSVC.class).installIntoWorld(sender(), dimension, worldFolder);
if (installed == null) {
sender().sendMessage(C.RED + "Failed to stage world files for dimension \"" + dimension.getLoadKey() + "\".");
@@ -497,7 +516,6 @@ public class CommandIris implements DirectorExecutor {
@Param(description = "The name of the world to load")
String world
) {
World worldloaded = Bukkit.getWorld(world);
worldNameToCheck = world;
boolean worldExists = doesWorldExist(worldNameToCheck);
WorldEngine = world;
@@ -507,8 +525,7 @@ public class CommandIris implements DirectorExecutor {
return;
}
String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator;
File directory = new File(Bukkit.getWorldContainer(), pathtodim);
File directory = new File(IrisWorldStorage.packRoot(IrisWorldStorage.keyFromLegacyName(world)), "dimensions");
String dimension = null;
if (directory.exists() && directory.isDirectory()) {
@@ -562,8 +579,7 @@ public class CommandIris implements DirectorExecutor {
}
boolean doesWorldExist(String worldName) {
File worldContainer = Bukkit.getWorldContainer();
File worldDirectory = new File(worldContainer, worldName);
File worldDirectory = IrisWorldStorage.dimensionRoot(worldName);
return worldDirectory.exists() && worldDirectory.isDirectory();
}
@@ -42,6 +42,8 @@ import art.arcane.volmlib.util.director.annotations.Param;
import art.arcane.iris.core.tools.IrisToolbelt;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.volmlib.util.math.RNG;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
@@ -49,6 +51,7 @@ import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.structure.Structure;
import org.bukkit.util.StructureSearchResult;
import java.io.File;
@@ -139,8 +142,9 @@ public class CommandStructure implements DirectorExecutor {
int irisPlaced = 0;
KList<String> notFound = new KList<>();
KList<String> cannotGenerate = new KList<>();
for (org.bukkit.generator.structure.Structure structure : Registry.STRUCTURE) {
NamespacedKey key = structure.getKey();
Registry<Structure> structureRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.STRUCTURE);
for (Structure structure : structureRegistry) {
NamespacedKey key = structureRegistry.getKey(structure);
String keyName = key == null ? structure.toString() : key.toString();
boolean isIrisPlaced = engine != null && IrisStructureLocator.suppressesVanilla(engine, keyName);
boolean isReachable = engine != null && reachable.contains(keyName.toLowerCase());
@@ -389,7 +389,7 @@ public class IrisEngineSVC implements IrisService {
}
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
return pregeneratorJob != null && pregeneratorJob.targetsWorldName(engine.getWorld().name());
return pregeneratorJob != null && pregeneratorJob.targetsWorldIdentity(engine.getWorld().identity());
}
private int activeTectonicLimit(Engine engine) {
@@ -446,7 +446,7 @@ public class IrisEngineSVC implements IrisService {
}
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
boolean pregeneratorTargetsWorld = pregeneratorJob != null && pregeneratorJob.targetsWorldName(world.getName());
boolean pregeneratorTargetsWorld = pregeneratorJob != null && pregeneratorJob.targetsWorld(world);
return shouldSkipMantleReductionForMaintenance(maintenanceActive, pregeneratorTargetsWorld);
}
}
@@ -32,6 +32,7 @@ import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.spi.protocol.IrisProtocol;
import art.arcane.iris.util.common.plugin.IrisService;
import art.arcane.iris.util.common.scheduling.J;
import art.arcane.volmlib.util.bukkit.WorldIdentity;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
@@ -167,7 +168,7 @@ public class IrisProtocolService implements IrisService, PluginMessageListener,
world.getSeed(), engine.getMinHeight(), engine.getMaxHeight(), true);
return;
}
protocol.sendDimensionStatus(sessionId, world.getName(), "", world.getSeed(), world.getMinHeight(), world.getMaxHeight(), false);
protocol.sendDimensionStatus(sessionId, WorldIdentity.serialize(world), "", world.getSeed(), world.getMinHeight(), world.getMaxHeight(), false);
}
private static Engine resolveEngine(String sessionId) {
@@ -24,6 +24,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.World;
import art.arcane.iris.Iris;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.edit.DustRevealer;
@@ -32,6 +33,7 @@ import art.arcane.iris.core.wand.WandSelection;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.util.project.matter.WorldMatter;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.bukkit.WorldIdentity;
import art.arcane.volmlib.util.data.Cuboid;
import art.arcane.iris.util.common.format.C;
import art.arcane.volmlib.util.math.M;
@@ -210,7 +212,9 @@ public class WandSVC implements IrisService {
if (f.length != 2) return null;
String[] g = f[0].split("\\Q,\\E");
if (g.length != 3) return null;
return new Location(Bukkit.getWorld(f[1]), Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2]));
World world = WorldIdentity.resolve(f[1]).orElse(null);
if (world == null) return null;
return new Location(world, Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2]));
} catch (Throwable e) {
Iris.reportError(e);
return null;
@@ -228,7 +232,7 @@ public class WandSVC implements IrisService {
return "<#>";
}
return loc.getBlockX() + "," + loc.getBlockY() + "," + loc.getBlockZ() + " in " + loc.getWorld().getName();
return loc.getBlockX() + "," + loc.getBlockY() + "," + loc.getBlockZ() + " in " + WorldIdentity.serialize(loc.getWorld());
}
/**
@@ -532,7 +536,7 @@ public class WandSVC implements IrisService {
Location[] f = getCuboidFromItem(item);
Location other = left ? f[1] : f[0];
if (other != null && !other.getWorld().getName().equals(a.getWorld().getName())) {
if (other != null && !WorldIdentity.key(other.getWorld()).equals(WorldIdentity.key(a.getWorld()))) {
other = null;
}