mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 12:41:43 +00:00
ddd
This commit is contained in:
@@ -117,7 +117,9 @@ public final class IrisWorldStorage {
|
||||
throw new IllegalArgumentException("World identifier is invalid: " + requestedName);
|
||||
}
|
||||
} else {
|
||||
key = keyFromName(requestedName, levelName);
|
||||
// "iris worlds" and Multiverse print the Bukkit startup name (<level>_iris_<key>), so
|
||||
// accept that form here instead of turning it into iris:<level>_iris_<key>.
|
||||
key = keyFromConfiguredWorldName(requestedName, levelName);
|
||||
}
|
||||
|
||||
if (!IRIS_NAMESPACE.equals(key.getNamespace()) || !key.getKey().matches("[a-z0-9_-]+")) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.WorldCreator;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* WorldCreator.ofKey and WorldCreator#key are Paper-API-only. Once a call throws
|
||||
@@ -12,31 +15,40 @@ import java.io.File;
|
||||
* straight to the fallback. The fallback derives names/keys through IrisWorldStorage's
|
||||
* current configured-name mapping so persistent Spigot worlds round-trip without changing
|
||||
* their startup directory.
|
||||
*
|
||||
* <p>Paper names a keyed creator {@code <namespace>_<key>} and refuses a creator built from a name
|
||||
* and a key together, but it names that same world {@code <level>_<namespace>_<key>} when it loads
|
||||
* it out of {@code <level>/dimensions/<namespace>/<key>} on every later boot. A persistent world
|
||||
* therefore has to be born under the startup name, which is what {@link #ofPersistentKey} does.</p>
|
||||
*/
|
||||
public final class WorldCreatorCompat {
|
||||
private static volatile boolean keyedCreatorsUnavailable;
|
||||
private static volatile boolean creatorNamesUnwritable;
|
||||
private static volatile Field creatorNameField;
|
||||
|
||||
private WorldCreatorCompat() {
|
||||
}
|
||||
|
||||
public static WorldCreator ofKey(NamespacedKey worldKey) {
|
||||
return ofKey(worldKey, IrisWorldStorage.logicalName(worldKey));
|
||||
}
|
||||
|
||||
public static WorldCreator ofKey(NamespacedKey worldKey, String fallbackWorldName) {
|
||||
WorldCreator keyedCreator = keyedCreator(worldKey);
|
||||
if (keyedCreator != null) {
|
||||
return keyedCreator;
|
||||
}
|
||||
return new WorldCreator(fallbackWorldName);
|
||||
return new WorldCreator(IrisWorldStorage.logicalName(worldKey));
|
||||
}
|
||||
|
||||
public static WorldCreator ofKey(NamespacedKey worldKey, String worldName) {
|
||||
String name = requireWorldName(worldName);
|
||||
WorldCreator keyedCreator = keyedCreator(worldKey);
|
||||
if (keyedCreator == null) {
|
||||
return new WorldCreator(name);
|
||||
}
|
||||
renameCreator(keyedCreator, name);
|
||||
return keyedCreator;
|
||||
}
|
||||
|
||||
public static WorldCreator ofPersistentKey(NamespacedKey worldKey) {
|
||||
WorldCreator keyedCreator = keyedCreator(worldKey);
|
||||
if (keyedCreator != null) {
|
||||
return keyedCreator;
|
||||
}
|
||||
return new WorldCreator(fallbackPersistentName(worldKey, IrisWorldStorage.levelRoot().getName()));
|
||||
return ofKey(worldKey, persistentWorldName(worldKey, IrisWorldStorage.levelRoot().getName()));
|
||||
}
|
||||
|
||||
public static File persistentDimensionRoot(NamespacedKey worldKey) {
|
||||
@@ -75,18 +87,10 @@ public final class WorldCreatorCompat {
|
||||
);
|
||||
}
|
||||
|
||||
static String fallbackName(NamespacedKey worldKey, String levelName) {
|
||||
return IrisWorldStorage.logicalName(worldKey, levelName);
|
||||
}
|
||||
|
||||
static String fallbackPersistentName(NamespacedKey worldKey, String levelName) {
|
||||
static String persistentWorldName(NamespacedKey worldKey, String levelName) {
|
||||
return IrisWorldStorage.configuredWorldName(worldKey, levelName);
|
||||
}
|
||||
|
||||
static NamespacedKey fallbackKey(String creatorName, String levelName) {
|
||||
return IrisWorldStorage.keyFromConfiguredWorldName(creatorName, levelName);
|
||||
}
|
||||
|
||||
private static WorldCreator keyedCreator(NamespacedKey worldKey) {
|
||||
if (keyedCreatorsUnavailable) {
|
||||
return null;
|
||||
@@ -98,4 +102,42 @@ public final class WorldCreatorCompat {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WorldCreator rejects a name and a key passed together and derives the name from the key, so the
|
||||
* only way to hand a keyed world its startup name at creation is to overwrite the creator's name.
|
||||
* If that ever stops working the keyed creator is still correct except for the name, which is what
|
||||
* shipped before, so the world is created rather than refused.
|
||||
*/
|
||||
private static void renameCreator(WorldCreator creator, String worldName) {
|
||||
if (worldName.equals(creator.name()) || creatorNamesUnwritable) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
creatorNameField().set(creator, worldName);
|
||||
} catch (ReflectiveOperationException | RuntimeException | Error e) {
|
||||
creatorNamesUnwritable = true;
|
||||
IrisLogging.warn("WorldCreator name is not writable; world %s is created as %s and renamed on the next boot.",
|
||||
worldName, creator.name());
|
||||
}
|
||||
}
|
||||
|
||||
private static Field creatorNameField() throws ReflectiveOperationException {
|
||||
Field cached = creatorNameField;
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
Field name = WorldCreator.class.getDeclaredField("name");
|
||||
name.setAccessible(true);
|
||||
creatorNameField = name;
|
||||
return name;
|
||||
}
|
||||
|
||||
private static String requireWorldName(String worldName) {
|
||||
String name = Objects.requireNonNull(worldName, "worldName").trim();
|
||||
if (name.isEmpty()) {
|
||||
throw new IllegalArgumentException("World name cannot be empty.");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
package art.arcane.iris.core.link;
|
||||
|
||||
import art.arcane.iris.core.IrisWorldStorage;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.mvplugins.multiverse.core.MultiverseCoreApi;
|
||||
import org.mvplugins.multiverse.core.utils.result.Attempt;
|
||||
@@ -30,57 +33,146 @@ import org.mvplugins.multiverse.core.world.options.RemoveWorldOptions;
|
||||
import org.mvplugins.multiverse.core.world.reasons.RemoveFailureReason;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Multiverse indexes its world store by NamespacedKey and by the Bukkit world name it saw when the
|
||||
* world was imported, and records that name as {@code legacy-world-name}. Iris creates and registers
|
||||
* its worlds under the Paper startup name ({@code <level>_<namespace>_<key>}), so the name Multiverse
|
||||
* sees at import is already the name it will see again after every restart, and it is the name every
|
||||
* Iris unregistration uses.
|
||||
*/
|
||||
public class MultiverseCoreLink {
|
||||
public boolean removeFromConfig(World world) {
|
||||
return removeFromConfig(world.getName());
|
||||
}
|
||||
|
||||
public boolean removeFromConfig(String world) {
|
||||
/**
|
||||
* Best-effort scrub for worlds Iris never registered (studio worlds). Multiverse not knowing the
|
||||
* world is the normal case here, so a miss is not worth a warning.
|
||||
*/
|
||||
public boolean removeIfPresent(World world) {
|
||||
String worldName = Objects.requireNonNull(world, "world").getName();
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
WorldManager manager = worldManager();
|
||||
MultiverseWorld multiverseWorld = manager.getWorld(world).getOrElse((MultiverseWorld) null);
|
||||
MultiverseWorld multiverseWorld = resolve(manager, worldName);
|
||||
if (multiverseWorld == null) {
|
||||
return false;
|
||||
}
|
||||
return remove(manager, multiverseWorld, worldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authoritative unregistration for a world Iris registered. A miss leaves a ghost entry behind in
|
||||
* Multiverse's worlds.yml, so it is reported.
|
||||
*/
|
||||
public boolean removeFromConfig(String configuredWorldName) {
|
||||
String worldName = requireWorldName(configuredWorldName);
|
||||
if (!isActive()) {
|
||||
IrisLogging.debug("Multiverse is not enabled; skipped unregistering \"" + worldName + "\".");
|
||||
return false;
|
||||
}
|
||||
WorldManager manager = worldManager();
|
||||
MultiverseWorld multiverseWorld = resolve(manager, worldName);
|
||||
if (multiverseWorld == null) {
|
||||
IrisLogging.warn("Multiverse has no world registered as %s; its worlds.yml entry was left as-is.",
|
||||
worldName);
|
||||
return false;
|
||||
}
|
||||
return remove(manager, multiverseWorld, worldName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void updateWorld(World bukkitWorld, String configuredWorldName, String pack) {
|
||||
World world = Objects.requireNonNull(bukkitWorld, "bukkitWorld");
|
||||
String worldName = requireWorldName(configuredWorldName);
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
if (!worldName.equals(world.getName())) {
|
||||
// Multiverse records the live name as legacy-world-name. A live name that is not the
|
||||
// startup name makes it re-import the world next boot and collide with its own config key.
|
||||
IrisLogging.warn("World %s is live as %s; Multiverse will record the live name.",
|
||||
worldName, world.getName());
|
||||
}
|
||||
String generator = "Iris:" + pack;
|
||||
WorldManager manager = worldManager();
|
||||
MultiverseWorld multiverseWorld = manager.getWorld(world)
|
||||
.orElse(() -> manager.getWorld(worldName))
|
||||
.getOrElse(() -> {
|
||||
// Import through the live world so Multiverse binds its own config key to the key
|
||||
// Paper gave the world and records the startup name the world was created under.
|
||||
ImportWorldOptions options = ImportWorldOptions.worldName(world.getName())
|
||||
.generator(generator)
|
||||
.environment(world.getEnvironment())
|
||||
.useSpawnAdjust(false);
|
||||
return manager.importWorld(options).get();
|
||||
});
|
||||
|
||||
multiverseWorld.setAutoLoad(false);
|
||||
if (!generator.equals(multiverseWorld.getGenerator())) {
|
||||
setWorldConfigString(multiverseWorld, "setGenerator", generator);
|
||||
}
|
||||
|
||||
manager.saveWorldsConfig().get();
|
||||
}
|
||||
|
||||
private boolean remove(WorldManager manager, MultiverseWorld multiverseWorld, String worldName) {
|
||||
Attempt<String, RemoveFailureReason> removal = manager.removeWorld(RemoveWorldOptions.world(multiverseWorld));
|
||||
if (removal.isFailure()) {
|
||||
throw new IllegalStateException("Multiverse refused to remove world \"" + world + "\": "
|
||||
throw new IllegalStateException("Multiverse refused to remove world \"" + worldName + "\": "
|
||||
+ removal.getFailureMessage());
|
||||
}
|
||||
manager.saveWorldsConfig().get();
|
||||
return true;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void updateWorld(World bukkitWorld, String pack) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
private MultiverseWorld resolve(WorldManager manager, String worldName) {
|
||||
for (String candidate : lookupNames(worldName, IrisWorldStorage.levelRoot().getName())) {
|
||||
MultiverseWorld multiverseWorld = manager.getWorld(candidate).getOrElse((MultiverseWorld) null);
|
||||
if (multiverseWorld != null) {
|
||||
return multiverseWorld;
|
||||
}
|
||||
}
|
||||
String generator = "Iris:" + pack;
|
||||
WorldManager manager = worldManager();
|
||||
MultiverseWorld multiverseWorld = manager.getWorld(bukkitWorld).getOrElse(() -> {
|
||||
ImportWorldOptions options = ImportWorldOptions.worldName(bukkitWorld.getName())
|
||||
.generator(generator)
|
||||
.environment(bukkitWorld.getEnvironment())
|
||||
.useSpawnAdjust(false);
|
||||
return manager.importWorld(options).get();
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
multiverseWorld.setAutoLoad(false);
|
||||
if (!generator.equals(multiverseWorld.getGenerator())) {
|
||||
Field field = MultiverseWorld.class.getDeclaredField("worldConfig");
|
||||
field.setAccessible(true);
|
||||
|
||||
Object config = field.get(multiverseWorld);
|
||||
config.getClass()
|
||||
.getDeclaredMethod("setGenerator", String.class)
|
||||
.invoke(config, generator);
|
||||
/**
|
||||
* worlds.yml files written by Iris builds that created keyed worlds under {@code <namespace>_<key>}
|
||||
* still carry that name, so a startup name must also resolve through the Multiverse config key it
|
||||
* encodes or those entries can never be removed.
|
||||
*/
|
||||
static List<String> lookupNames(String worldName, String levelName) {
|
||||
List<String> candidates = new ArrayList<>(2);
|
||||
candidates.add(worldName);
|
||||
try {
|
||||
NamespacedKey key = IrisWorldStorage.managedKeyFromName(worldName, levelName);
|
||||
if (IrisWorldStorage.configuredWorldName(key, levelName).equals(worldName)) {
|
||||
candidates.add(key.toString());
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// Not an Iris startup name; the plain name is the only identity Multiverse can have.
|
||||
}
|
||||
return List.copyOf(candidates);
|
||||
}
|
||||
|
||||
manager.saveWorldsConfig().get();
|
||||
private static void setWorldConfigString(MultiverseWorld world, String setter, String value) throws Exception {
|
||||
Field field = MultiverseWorld.class.getDeclaredField("worldConfig");
|
||||
field.setAccessible(true);
|
||||
|
||||
Object config = field.get(world);
|
||||
Method method = config.getClass().getDeclaredMethod(setter, String.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(config, value);
|
||||
}
|
||||
|
||||
private static String requireWorldName(String worldName) {
|
||||
String name = Objects.requireNonNull(worldName, "worldName").trim();
|
||||
if (name.isEmpty()) {
|
||||
throw new IllegalArgumentException("World name cannot be empty.");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private WorldManager worldManager() {
|
||||
|
||||
@@ -750,7 +750,7 @@ public final class StudioOpenCoordinator {
|
||||
for (World loadedWorld : loadedWorlds) {
|
||||
CompletableFuture<Boolean> unload = J.sfut(() ->
|
||||
IrisServices.get(MultiverseCoreLink.class)
|
||||
.removeFromConfig(loadedWorld))
|
||||
.removeIfPresent(loadedWorld))
|
||||
.thenCompose(ignored -> WorldLifecycleService.get().unloadAsync(loadedWorld, false));
|
||||
unloads.add(unload);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public final class WorldRuntimeControlService {
|
||||
return false;
|
||||
}
|
||||
|
||||
IrisServices.get(art.arcane.iris.core.link.MultiverseCoreLink.class).removeFromConfig(world);
|
||||
IrisServices.get(art.arcane.iris.core.link.MultiverseCoreLink.class).removeIfPresent(world);
|
||||
setIntGameRule(world, 0, "SPAWN_CHUNK_RADIUS", "spawnChunkRadius");
|
||||
enableStudioEntitySpawning(world);
|
||||
if (!IrisSettings.get().getStudio().isDisableTimeAndWeather()) {
|
||||
|
||||
@@ -331,16 +331,23 @@ public class IrisCreator {
|
||||
reportCreationProgress(creationReporter, 0.84D, "register_world");
|
||||
|
||||
if (!studio && !benchmark) {
|
||||
// bukkit.yml and Multiverse must agree on the startup name, or Multiverse re-imports the
|
||||
// world on the next boot and collides with its own config key.
|
||||
String registrationName = IrisWorldStorage.configuredWorldName(
|
||||
worldKey,
|
||||
IrisWorldStorage.levelRoot().getName()
|
||||
);
|
||||
BukkitWorldConfiguration.register(
|
||||
BUKKIT_YML,
|
||||
IrisWorldStorage.configuredWorldName(worldKey, IrisWorldStorage.levelRoot().getName()),
|
||||
registrationName,
|
||||
dimension,
|
||||
seed
|
||||
);
|
||||
bukkitRegistered = true;
|
||||
World createdWorld = world;
|
||||
CompletableFuture<Void> multiverseRegistration = J.sfut(
|
||||
() -> IrisServices.get(MultiverseCoreLink.class).updateWorld(createdWorld, dimension)
|
||||
() -> IrisServices.get(MultiverseCoreLink.class)
|
||||
.updateWorld(createdWorld, registrationName, dimension)
|
||||
);
|
||||
if (multiverseRegistration == null) {
|
||||
throw new IrisException("Failed to schedule Multiverse registration for world \"" + name + "\".");
|
||||
|
||||
@@ -172,6 +172,18 @@ public class IrisWorldStorageTest {
|
||||
() -> IrisWorldStorage.managedKeyFromName("iris:unsafe.world", "world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void managedKeyAcceptsTheConfiguredBukkitStartupName() {
|
||||
assertEquals(new NamespacedKey("iris", "mvtest"),
|
||||
IrisWorldStorage.managedKeyFromName("world_iris_mvtest", "world"));
|
||||
assertEquals(new NamespacedKey("iris", "mvtest"),
|
||||
IrisWorldStorage.managedKeyFromName("survival_iris_mvtest", "survival"));
|
||||
assertEquals(new NamespacedKey("iris", "iris_mvtest"),
|
||||
IrisWorldStorage.managedKeyFromName("iris_mvtest", "world"));
|
||||
assertEquals(new NamespacedKey("iris", "world_iris_mvtest"),
|
||||
IrisWorldStorage.managedKeyFromName("world_iris_mvtest", "survival"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitManagedIdentityDoesNotRequireServerLevelLookup() {
|
||||
NamespacedKey worldKey = new NamespacedKey("iris", "probe");
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
public class WorldCreatorCompatTest {
|
||||
@Test
|
||||
@@ -14,39 +16,48 @@ public class WorldCreatorCompatTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fallbackNameDerivesLogicalNameFromKey() {
|
||||
assertEquals("compat_world", WorldCreatorCompat.fallbackName(new NamespacedKey("iris", "compat_world"), "world"));
|
||||
assertEquals("world", WorldCreatorCompat.fallbackName(NamespacedKey.minecraft("overworld"), "world"));
|
||||
assertEquals("world_nether", WorldCreatorCompat.fallbackName(NamespacedKey.minecraft("the_nether"), "world"));
|
||||
assertEquals("world_the_end", WorldCreatorCompat.fallbackName(NamespacedKey.minecraft("the_end"), "world"));
|
||||
public void keyedCreatorIsBornUnderTheRequestedStartupName() {
|
||||
NamespacedKey key = new NamespacedKey("iris", "compat_world");
|
||||
|
||||
WorldCreator creator = WorldCreatorCompat.ofKey(key, "world_iris_compat_world");
|
||||
|
||||
assertEquals("world_iris_compat_world", creator.name());
|
||||
assertEquals(key, creator.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentFallbackUsesExactConfiguredStartupName() {
|
||||
public void keyedCreatorKeepsThePaperKeyNameWhenNoStartupNameIsGiven() {
|
||||
NamespacedKey key = new NamespacedKey("iris", "compat_world");
|
||||
|
||||
WorldCreator creator = WorldCreatorCompat.ofKey(key);
|
||||
|
||||
assertNotEquals("world_iris_compat_world", creator.name());
|
||||
assertEquals(key, creator.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentNameIsTheExactConfiguredStartupName() {
|
||||
assertEquals(
|
||||
"world_iris_compat_world",
|
||||
WorldCreatorCompat.fallbackPersistentName(new NamespacedKey("iris", "compat_world"), "world")
|
||||
WorldCreatorCompat.persistentWorldName(new NamespacedKey("iris", "compat_world"), "world")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fallbackKeyRoundTripsCreatorName() {
|
||||
assertEquals(new NamespacedKey("iris", "compat_world"), WorldCreatorCompat.fallbackKey("compat_world", "world"));
|
||||
assertEquals(NamespacedKey.minecraft("overworld"), WorldCreatorCompat.fallbackKey("world", "world"));
|
||||
assertEquals(NamespacedKey.minecraft("the_nether"), WorldCreatorCompat.fallbackKey("world_nether", "world"));
|
||||
assertEquals(
|
||||
new NamespacedKey("iris", "compat_world"),
|
||||
WorldCreatorCompat.fallbackKey("world_iris_compat_world", "world")
|
||||
);
|
||||
public void logicalNameDerivesTheLogicalNameFromKey() {
|
||||
assertEquals("compat_world", IrisWorldStorage.logicalName(new NamespacedKey("iris", "compat_world"), "world"));
|
||||
assertEquals("world", IrisWorldStorage.logicalName(NamespacedKey.minecraft("overworld"), "world"));
|
||||
assertEquals("world_nether", IrisWorldStorage.logicalName(NamespacedKey.minecraft("the_nether"), "world"));
|
||||
assertEquals("world_the_end", IrisWorldStorage.logicalName(NamespacedKey.minecraft("the_end"), "world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fallbackMappingIsStableAcrossRoundTrips() {
|
||||
NamespacedKey key = new NamespacedKey("iris", "iris_world");
|
||||
public void startupNameRoundTripsBackToTheWorldKey() {
|
||||
NamespacedKey key = new NamespacedKey("iris", "compat_world");
|
||||
|
||||
String name = WorldCreatorCompat.fallbackName(key, "world");
|
||||
String name = WorldCreatorCompat.persistentWorldName(key, "world");
|
||||
|
||||
assertEquals(key, WorldCreatorCompat.fallbackKey(name, "world"));
|
||||
assertEquals(name, WorldCreatorCompat.fallbackName(key, "world"));
|
||||
assertEquals("world_iris_compat_world", name);
|
||||
assertEquals(key, IrisWorldStorage.keyFromConfiguredWorldName(name, "world"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,22 @@ public class IrisWorldRemovalServiceTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removalAcceptsTheConfiguredStartupNameThatIrisWorldsPrints() throws Exception {
|
||||
Path levelRoot = temporaryFolder.newFolder("world").toPath();
|
||||
WorldRemovalPathPolicy.Target logical = WorldRemovalPathPolicy.resolve("moon", "world", levelRoot);
|
||||
WorldRemovalPathPolicy.Target configured =
|
||||
WorldRemovalPathPolicy.resolve("world_iris_moon", "world", levelRoot);
|
||||
|
||||
assertEquals(logical.worldKey(), configured.worldKey());
|
||||
assertEquals("moon", configured.logicalName());
|
||||
assertEquals(logical.worldDirectory(), configured.worldDirectory());
|
||||
assertEquals(
|
||||
"world_iris_moon",
|
||||
IrisWorldRemovalService.bukkitConfigurationWorldName(configured)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diskInspectionReadsOnlyCanonicalPaperStartupSection() throws Exception {
|
||||
Path levelRoot = temporaryFolder.newFolder("inspection-world").toPath();
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package art.arcane.iris.core.lifecycle;
|
||||
|
||||
import art.arcane.iris.core.WorldCreatorCompat;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class WorldLifecycleRequestTest {
|
||||
@Test
|
||||
public void requestCarriesTheStartupNameTheCreatorWasBornWith() {
|
||||
NamespacedKey worldKey = new NamespacedKey("iris", "mvtest");
|
||||
WorldCreator creator = WorldCreatorCompat.ofKey(worldKey, "world_iris_mvtest")
|
||||
.environment(World.Environment.NORMAL)
|
||||
.seed(99L);
|
||||
|
||||
WorldLifecycleRequest request = WorldLifecycleRequest.fromCreator(
|
||||
creator,
|
||||
false,
|
||||
false,
|
||||
WorldLifecycleCaller.CREATE);
|
||||
|
||||
assertEquals("world_iris_mvtest", request.worldName());
|
||||
assertEquals(worldKey, request.worldKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rebuiltCreatorKeepsBothTheStartupNameAndTheWorldKey() {
|
||||
NamespacedKey worldKey = new NamespacedKey("iris", "mvtest");
|
||||
WorldLifecycleRequest request = WorldLifecycleRequest.fromCreator(
|
||||
WorldCreatorCompat.ofKey(worldKey, "world_iris_mvtest")
|
||||
.environment(World.Environment.NORMAL)
|
||||
.seed(99L),
|
||||
false,
|
||||
false,
|
||||
WorldLifecycleCaller.CREATE);
|
||||
|
||||
WorldCreator rebuilt = request.toWorldCreator();
|
||||
|
||||
assertEquals("world_iris_mvtest", rebuilt.name());
|
||||
assertEquals(worldKey, rebuilt.key());
|
||||
assertEquals(99L, rebuilt.seed());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package art.arcane.iris.core.link;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MultiverseCoreLinkTest {
|
||||
@Test
|
||||
public void multiverseRegistrationRequiresTheConfiguredWorldName() throws Exception {
|
||||
Method updateWorld = MultiverseCoreLink.class
|
||||
.getDeclaredMethod("updateWorld", World.class, String.class, String.class);
|
||||
|
||||
assertEquals(void.class, updateWorld.getReturnType());
|
||||
assertFalse("updateWorld must not keep an overload that derives the name from the live world",
|
||||
Arrays.stream(MultiverseCoreLink.class.getDeclaredMethods())
|
||||
.anyMatch(method -> "updateWorld".equals(method.getName())
|
||||
&& method.getParameterCount() == 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationDoesNotRewriteTheRecordedMultiverseName() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/link/MultiverseCoreLink.java"));
|
||||
|
||||
assertFalse("worlds are created under the startup name, so the recorded name needs no correction",
|
||||
source.contains("setLegacyWorldName"));
|
||||
assertTrue("Multiverse still has to be told the live world name it will record",
|
||||
source.contains("ImportWorldOptions.worldName(world.getName())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupNamesAddTheIrisKeyForConfiguredStartupNames() {
|
||||
assertEquals(
|
||||
List.of("world_iris_mvtest", "iris:mvtest"),
|
||||
MultiverseCoreLink.lookupNames("world_iris_mvtest", "world")
|
||||
);
|
||||
assertEquals(
|
||||
List.of("survival_iris_mvtest", "iris:mvtest"),
|
||||
MultiverseCoreLink.lookupNames("survival_iris_mvtest", "survival")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupNamesKeepNonConfiguredNamesUntouched() {
|
||||
assertEquals(List.of("iris_mvtest"), MultiverseCoreLink.lookupNames("iris_mvtest", "world"));
|
||||
assertEquals(List.of("world"), MultiverseCoreLink.lookupNames("world", "world"));
|
||||
assertEquals(List.of("iris:mvtest"), MultiverseCoreLink.lookupNames("iris:mvtest", "world"));
|
||||
assertEquals(List.of("iris_studio-demo"), MultiverseCoreLink.lookupNames("iris_studio-demo", "world"));
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package art.arcane.iris.core.tools;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisCreatorMultiverseRegistrationContractTest {
|
||||
@Test
|
||||
public void bukkitAndMultiverseRegistrationShareOneConfiguredName() throws IOException {
|
||||
String source = compact(Files.readString(Path.of(System.getProperty("iris.irisCreatorSource"))));
|
||||
|
||||
assertTrue("bukkit.yml must be registered under the shared configured name",
|
||||
source.contains("BukkitWorldConfiguration.register(BUKKIT_YML,registrationName,dimension,seed)"));
|
||||
assertTrue("Multiverse must be registered under the same configured name",
|
||||
source.contains(".updateWorld(createdWorld,registrationName,dimension)"));
|
||||
assertFalse("Multiverse must never be registered under the transient created-world name",
|
||||
source.contains(".updateWorld(createdWorld,dimension)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiverseRollbackUsesTheSameConfiguredName() throws IOException {
|
||||
String source = compact(Files.readString(Path.of(System.getProperty("iris.irisCreatorSource"))));
|
||||
|
||||
assertTrue("rollback must unregister the name creation registered",
|
||||
source.contains(".removeFromConfig(IrisWorldStorage.configuredWorldName(worldKey,"
|
||||
+ "IrisWorldStorage.levelRoot().getName()))"));
|
||||
}
|
||||
|
||||
private static String compact(String source) {
|
||||
return source.replaceAll("\\s+", "");
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertThrows;
|
||||
public class WorldHandlerTest {
|
||||
@Test
|
||||
public void resolvesOwnedIrisWorldByLogicalNameAndCanonicalKey() throws DirectorParsingException {
|
||||
World world = world("iris_irisworld", new NamespacedKey("iris", "irisworld"));
|
||||
World world = world("world_iris_irisworld", new NamespacedKey("iris", "irisworld"));
|
||||
WorldHandler handler = new TestWorldHandler(List.of(world));
|
||||
|
||||
assertSame(world, handler.parse("irisworld", false));
|
||||
|
||||
Reference in New Issue
Block a user