From 0c9ae9ed4d5e8f759756f717c078514f816a4ed9 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Thu, 15 Oct 2026 09:19:01 -0400 Subject: [PATCH 1/2] Fixed modded world listing - /iris worlds and /iris info report each Iris world's own seed instead of the server level seed - /iris worlds and /iris info report the world's persisted generated chunk total instead of the per-session counter - /iris seed reports the Iris world seed on its World seed line - Added command tests for per-world seeds, persisted chunk totals and the gamemaster seed gate --- .../modded/command/IrisModdedCommands.java | 6 +- .../command/IrisModdedWorldListingTest.java | 181 ++++++++++++++++++ 2 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java index 562c5fb53..da7a77163 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java @@ -301,9 +301,9 @@ public final class IrisModdedCommands { String featureStatus = irisGenerator.importedFeaturesStatus(); lines.add(irisIdentity + ": pack=" + engine.getDimension().getLoadKey() + " world=" + dimensionId - + (showSeed ? " seed=" + level.seed() : "") + + (showSeed ? " seed=" + engine.getWorld().getRawWorldSeed() : "") + " height=" + engine.getMinHeight() + ".." + engine.getMaxHeight() - + " generated=" + engine.getGenerated() + + " generated=" + engine.getEngineData().getStatistics().getChunksGenerated() + (featureStatus == null ? "" : " importedFeatures=" + featureStatus) + " data=" + engine.getData().getDataFolder().getName()); } @@ -329,7 +329,7 @@ public final class IrisModdedCommands { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_14)); return 0; } - ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, MessageArgument.untrusted("value", level.seed()))); + ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, MessageArgument.untrusted("value", engine.getWorld().getRawWorldSeed()))); ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_SEED_MIXED, MessageArgument.untrusted("value", engine.getSeedManager().getSeed()), MessageArgument.untrusted("value2", engine.getSeedManager().getFullMixedSeed()))); return 1; } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java new file mode 100644 index 000000000..5b64ff36f --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java @@ -0,0 +1,181 @@ +package art.arcane.iris.modded.command; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.IrisEngineData; +import art.arcane.iris.generation.runtime.SeedManager; +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.iris.localization.IrisLanguage; +import art.arcane.iris.modded.IrisModdedChunkGenerator; +import art.arcane.iris.modded.localization.ModdedCommandMessages; +import art.arcane.iris.pack.loading.IrisData; +import art.arcane.iris.world.IrisWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.util.localization.MessageArgument; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.invocation.InvocationOnMock; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +public class IrisModdedWorldListingTest { + private static final long SERVER_SEED = 1111L; + + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void worldsListsEachIrisWorldWithItsOwnSeedAndPersistedChunkCount() { + NativeWorld alpha = level("iris:alpha"); + NativeWorld beta = level("iris:beta"); + NativeWorld vanilla = level("minecraft:the_end"); + NativeModdedServer server = mock(NativeModdedServer.class); + when(server.worlds()).thenReturn(List.of(alpha, beta, vanilla)); + List output = new ArrayList<>(); + NativeCommandSource source = source(server, null, true, output); + IrisModdedChunkGenerator alphaGenerator = generator("overworld:overworld", engine(424242L, 1089)); + IrisModdedChunkGenerator betaGenerator = generator("overworld:overworld", engine(-77L, 0)); + + try (MockedStatic generators = mockStatic(NativeWorldGenerators.class)) { + generators.when(() -> NativeWorldGenerators.find(alpha, IrisModdedChunkGenerator.class)) + .thenReturn(alphaGenerator); + generators.when(() -> NativeWorldGenerators.find(beta, IrisModdedChunkGenerator.class)) + .thenReturn(betaGenerator); + + assertEquals(1, IrisModdedCommands.info(source, null)); + } + + String alphaRow = row(output, "world=iris:alpha"); + String betaRow = row(output, "world=iris:beta"); + assertTrue(alphaRow, alphaRow.contains(" seed=424242 ")); + assertTrue(alphaRow, alphaRow.contains(" generated=1089 ")); + assertTrue(betaRow, betaRow.contains(" seed=-77 ")); + assertTrue(betaRow, betaRow.contains(" generated=0 ")); + for (String line : output) { + assertFalse(line, line.contains("seed=" + SERVER_SEED)); + } + } + + @Test + public void worldsOmitsTheSeedForPlayersBelowGamemaster() { + NativeWorld alpha = level("iris:alpha"); + NativeModdedServer server = mock(NativeModdedServer.class); + when(server.worlds()).thenReturn(List.of(alpha)); + List output = new ArrayList<>(); + NativeCommandSource source = source(server, null, false, output); + IrisModdedChunkGenerator alphaGenerator = generator("overworld:overworld", engine(424242L, 1089)); + + try (MockedStatic generators = mockStatic(NativeWorldGenerators.class)) { + generators.when(() -> NativeWorldGenerators.find(alpha, IrisModdedChunkGenerator.class)) + .thenReturn(alphaGenerator); + + assertEquals(1, IrisModdedCommands.info(source, null)); + } + + String alphaRow = row(output, "world=iris:alpha"); + assertFalse(alphaRow, alphaRow.contains("seed=")); + assertTrue(alphaRow, alphaRow.contains(" generated=1089 ")); + } + + @Test + public void seedReportsTheIrisWorldSeedRatherThanTheServerSeed() { + NativeWorld alpha = level("iris:alpha"); + List output = new ArrayList<>(); + NativeCommandSource source = source(mock(NativeModdedServer.class), alpha, true, output); + Engine engine = engine(424242L, 1089); + IrisModdedChunkGenerator generator = generator("overworld:overworld", engine); + when(generator.commandEngine()).thenReturn(engine); + + try (MockedStatic generators = mockStatic(NativeWorldGenerators.class)) { + generators.when(() -> NativeWorldGenerators.find(alpha, IrisModdedChunkGenerator.class)) + .thenReturn(generator); + + assertEquals(1, IrisModdedCommands.seed(source)); + } + + assertTrue(output.toString(), output.contains(IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, + MessageArgument.untrusted("value", 424242L)))); + for (String line : output) { + assertFalse(line, line.contains(Long.toString(SERVER_SEED))); + } + } + + private static NativeWorld level(String name) { + NativeWorld level = mock(NativeWorld.class); + when(level.name()).thenReturn(name); + when(level.seed()).thenReturn(SERVER_SEED); + return level; + } + + private static NativeCommandSource source(NativeModdedServer server, NativeWorld world, boolean gamemaster, List output) { + NativeCommandSource source = mock(NativeCommandSource.class); + when(source.server()).thenReturn(server); + when(source.world()).thenReturn(world); + when(source.isGameMaster()).thenReturn(gamemaster); + doAnswer((InvocationOnMock invocation) -> { + Supplier text = invocation.getArgument(0); + output.add(text.get().getString()); + return null; + }).when(source).sendSuccess(any(), anyBoolean()); + return source; + } + + private static IrisModdedChunkGenerator generator(String dimensionKey, Engine engine) { + IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class); + when(generator.dimensionKey()).thenReturn(dimensionKey); + when(generator.engineIfBound()).thenReturn(engine); + return generator; + } + + private static Engine engine(long worldSeed, int chunksGenerated) { + IrisDimension dimension = mock(IrisDimension.class); + when(dimension.getLoadKey()).thenReturn("overworld"); + IrisData data = mock(IrisData.class); + when(data.getDataFolder()).thenReturn(new File("overworld")); + SeedManager seeds = mock(SeedManager.class); + when(seeds.getSeed()).thenReturn(worldSeed); + IrisEngineData engineData = new IrisEngineData(); + engineData.getStatistics().setChunksGenerated(chunksGenerated); + Engine engine = mock(Engine.class); + when(engine.getDimension()).thenReturn(dimension); + when(engine.getData()).thenReturn(data); + when(engine.getSeedManager()).thenReturn(seeds); + when(engine.getWorld()).thenReturn(IrisWorld.builder().name("alpha").seed(worldSeed).minHeight(-64).maxHeight(320).build()); + when(engine.getMinHeight()).thenReturn(-64); + when(engine.getMaxHeight()).thenReturn(320); + when(engine.getEngineData()).thenReturn(engineData); + when(engine.getGenerated()).thenReturn(0); + return engine; + } + + private static String row(List output, String marker) { + for (String line : output) { + if (line.contains(marker + " ")) { + return line; + } + } + throw new AssertionError("No row containing '" + marker + "' in " + output); + } +} From 9a241b85cbd99bb085cea2ee753edf7c1898f425 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Thu, 15 Oct 2026 09:29:56 -0400 Subject: [PATCH 2/2] Labeled world rows by dimension - /iris worlds and /iris info label each row with its own dimension key instead of the shared generator identity - Dropped the redundant world= field now that the row label carries the dimension key - Rows for worlds whose engine has not started use the same dimension key label - Listing tests assert per-world row labels, including an unstarted engine row --- .../modded/command/IrisModdedCommands.java | 6 +-- .../command/IrisModdedWorldListingTest.java | 45 ++++++++++++++++--- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java index da7a77163..7e71c3413 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java @@ -294,13 +294,11 @@ public final class IrisModdedCommands { } Engine engine = irisGenerator.engineIfBound(); if (engine == null) { - lines.add(irisIdentity + ": pack=" + irisGenerator.dimensionKey() - + " world=" + dimensionId + " (engine not started yet)"); + lines.add(dimensionId + ": pack=" + irisGenerator.dimensionKey() + " (engine not started yet)"); continue; } String featureStatus = irisGenerator.importedFeaturesStatus(); - lines.add(irisIdentity + ": pack=" + engine.getDimension().getLoadKey() - + " world=" + dimensionId + lines.add(dimensionId + ": pack=" + engine.getDimension().getLoadKey() + (showSeed ? " seed=" + engine.getWorld().getRawWorldSeed() : "") + " height=" + engine.getMinHeight() + ".." + engine.getMaxHeight() + " generated=" + engine.getEngineData().getStatistics().getChunksGenerated() diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java index 5b64ff36f..6c8ba794a 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedWorldListingTest.java @@ -67,8 +67,8 @@ public class IrisModdedWorldListingTest { assertEquals(1, IrisModdedCommands.info(source, null)); } - String alphaRow = row(output, "world=iris:alpha"); - String betaRow = row(output, "world=iris:beta"); + String alphaRow = row(output, "iris:alpha"); + String betaRow = row(output, "iris:beta"); assertTrue(alphaRow, alphaRow.contains(" seed=424242 ")); assertTrue(alphaRow, alphaRow.contains(" generated=1089 ")); assertTrue(betaRow, betaRow.contains(" seed=-77 ")); @@ -78,6 +78,39 @@ public class IrisModdedWorldListingTest { } } + @Test + public void worldsLabelsEachRowWithItsOwnWorldKey() { + NativeWorld alpha = level("iris:alpha"); + NativeWorld beta = level("iris:beta"); + NativeWorld gamma = level("iris:gamma"); + NativeModdedServer server = mock(NativeModdedServer.class); + when(server.worlds()).thenReturn(List.of(alpha, beta, gamma)); + List output = new ArrayList<>(); + NativeCommandSource source = source(server, null, true, output); + IrisModdedChunkGenerator alphaGenerator = generator("overworld:overworld", engine(424242L, 1089)); + IrisModdedChunkGenerator betaGenerator = generator("overworld:overworld", engine(-77L, 0)); + IrisModdedChunkGenerator gammaGenerator = generator("overworld:overworld", null); + + try (MockedStatic generators = mockStatic(NativeWorldGenerators.class)) { + generators.when(() -> NativeWorldGenerators.find(alpha, IrisModdedChunkGenerator.class)) + .thenReturn(alphaGenerator); + generators.when(() -> NativeWorldGenerators.find(beta, IrisModdedChunkGenerator.class)) + .thenReturn(betaGenerator); + generators.when(() -> NativeWorldGenerators.find(gamma, IrisModdedChunkGenerator.class)) + .thenReturn(gammaGenerator); + + assertEquals(1, IrisModdedCommands.info(source, null)); + } + + assertTrue(row(output, "iris:alpha").startsWith("iris:alpha: pack=overworld ")); + assertTrue(row(output, "iris:beta").startsWith("iris:beta: pack=overworld ")); + assertEquals("iris:gamma: pack=overworld:overworld (engine not started yet)", row(output, "iris:gamma")); + for (String line : output) { + assertFalse(line, line.startsWith("iris:overworld")); + assertFalse(line, line.contains(" world=")); + } + } + @Test public void worldsOmitsTheSeedForPlayersBelowGamemaster() { NativeWorld alpha = level("iris:alpha"); @@ -94,7 +127,7 @@ public class IrisModdedWorldListingTest { assertEquals(1, IrisModdedCommands.info(source, null)); } - String alphaRow = row(output, "world=iris:alpha"); + String alphaRow = row(output, "iris:alpha"); assertFalse(alphaRow, alphaRow.contains("seed=")); assertTrue(alphaRow, alphaRow.contains(" generated=1089 ")); } @@ -170,12 +203,12 @@ public class IrisModdedWorldListingTest { return engine; } - private static String row(List output, String marker) { + private static String row(List output, String label) { for (String line : output) { - if (line.contains(marker + " ")) { + if (line.startsWith(label + ": ")) { return line; } } - throw new AssertionError("No row containing '" + marker + "' in " + output); + throw new AssertionError("No row labeled '" + label + "' in " + output); } }