Merged 1.21.1 mod targets

- Merge feature/modded-versions into the modded fix integration
- Take loader pins from gradle/mod-targets.json for every target
- Pin the 26.3 NeoForge target to 26.3.0.83
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-16 10:26:02 -04:00
commit b8aa00b70e
58 files changed
+3279 -133

No files matched your search

@@ -168,6 +168,12 @@ public final class ModdedEngineBootstrap {
ModdedStartup.prepareDimensionRestore(server);
}
public static void levelsReady(NativeModdedServer server) {
server.refreshWorlds();
bindWorldGenerators(server);
ModdedStartup.reinjectPersistentDimensions(server);
}
public static void serverStarted(NativeModdedServer server) {
// Prime the off-thread level snapshot before anything can read it; the per-tick refresh in
// ModdedScheduler.tick has not run yet at this point.
@@ -14,6 +14,7 @@ public final class ModdedLifecycleCallbacks {
return new NativeModdedCallbacks(
ModdedEngineBootstrap::serverAboutToStart,
ModdedEngineBootstrap::start,
ModdedEngineBootstrap::levelsReady,
ModdedEngineBootstrap::serverStarted,
ModdedEngineBootstrap::stop,
ModdedEngineBootstrap::stopped,
@@ -45,6 +45,9 @@ public final class ModdedMixinAudit {
private static final AtomicBoolean AUDITED = new AtomicBoolean(false);
static final List<ExpectedMixin> EXPECTED = List.of(
new ExpectedMixin(ENTITY_CONFIG, "ServerLevelsReadyMixin",
NativeMixinTarget.SERVER, "volmlib$levelsReady",
false, NativeMixinFlags::levelsReadyRan),
new ExpectedMixin(ENTITY_CONFIG, "RegistryClientNamesMixin",
NativeMixinTarget.REGISTRY_PACKET, "volmlib$clientRegistryNames",
false, NativeMixinFlags::registryClientNamesRan),
@@ -120,7 +120,6 @@ public final class ModdedStartup {
}
ModdedForcedDatapack.verifyInjected();
ModdedMixinAudit.runOnce();
reinjectPersistentDimensions(server);
ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull();
if (scheduler == null) {
@@ -114,15 +114,9 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
@Override
public void close() {
try {
if (!sync) {
try {
semaphore.tryAcquire(maxInFlight, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
ModdedIrisLog.info("Iris modded pregen done: dim={} completed={} peakInFlight={} finalLimit={}",
runtime.worldIdentity(), completed.get(), inFlightPeak.get(), adaptiveLimit.get());
awaitPendingChunks();
ModdedIrisLog.info("Iris modded pregen closing: dim={} completed={} pending={} peakInFlight={} finalLimit={}",
runtime.worldIdentity(), completed.get(), inFlight.get(), inFlightPeak.get(), adaptiveLimit.get());
try {
engine.syncGenerationHistory();
} catch (RuntimeException e) {
@@ -180,6 +174,39 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|| queuedFinalSave.get() != null;
}
private void awaitPendingChunks() {
if (sync || runtime.isServerThread()) {
return;
}
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSeconds)
+ TimeUnit.MILLISECONDS.toNanos(FINAL_SAVE_TIMEOUT_MILLIS);
boolean interrupted = false;
try {
while (inFlight.get() > 0) {
if (finalSaveOnServerThread.get() || !runtime.isRunning()) {
return;
}
if (System.nanoTime() >= deadline) {
throw new IllegalStateException("Iris pregen chunk callbacks did not finish for "
+ runtime.worldIdentity() + ": " + inFlight.get() + " pending");
}
try {
synchronized (permitMonitor) {
if (inFlight.get() > 0) {
permitMonitor.wait(FINAL_SAVE_POLL_MILLIS);
}
}
} catch (InterruptedException ignored) {
interrupted = true;
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
private void saveLevel(boolean wait) {
if (wait) {
saveFinalLevel();
@@ -237,30 +264,36 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
private void awaitFinalSave(FinalSaveRequest request) {
long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(FINAL_SAVE_TIMEOUT_MILLIS);
while (true) {
if (deferFinalSaveIfRequested()) {
return;
boolean interrupted = false;
try {
while (true) {
if (deferFinalSaveIfRequested()) {
return;
}
long remainingNanos = deadline - System.nanoTime();
if (remainingNanos <= 0L) {
ModdedIrisLog.warn("Iris pregen level save did not complete in time for {}", runtime.worldIdentity());
return;
}
long waitMillis = Math.max(1L, Math.min(FINAL_SAVE_POLL_MILLIS,
TimeUnit.NANOSECONDS.toMillis(remainingNanos)));
try {
request.completion().get(waitMillis, TimeUnit.MILLISECONDS);
return;
} catch (InterruptedException ignored) {
interrupted = true;
} catch (TimeoutException e) {
continue;
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
ModdedIrisLog.error("Iris pregen level save failed for {}", runtime.worldIdentity(), cause);
throw new IllegalStateException("Iris pregen level save failed for "
+ runtime.worldIdentity(), cause);
}
}
long remainingNanos = deadline - System.nanoTime();
if (remainingNanos <= 0L) {
ModdedIrisLog.warn("Iris pregen level save did not complete in time for {}", runtime.worldIdentity());
return;
}
long waitMillis = Math.max(1L, Math.min(FINAL_SAVE_POLL_MILLIS,
TimeUnit.NANOSECONDS.toMillis(remainingNanos)));
try {
request.completion().get(waitMillis, TimeUnit.MILLISECONDS);
return;
} catch (InterruptedException e) {
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
return;
} catch (TimeoutException e) {
continue;
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
ModdedIrisLog.error("Iris pregen level save failed for {}", runtime.worldIdentity(), cause);
throw new IllegalStateException("Iris pregen level save failed for "
+ runtime.worldIdentity(), cause);
}
}
}
@@ -0,0 +1,61 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer;
import org.junit.After;
import org.junit.Test;
import org.mockito.MockedStatic;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
public class ModdedStartupLifecycleTest {
@After
public void resetStartup() {
ModdedStartup.reset();
}
@Test
public void restoresAfterWorldsLoadWithoutRepeatingDuringServerTicks() {
NativeModdedServer server = mock(NativeModdedServer.class);
AtomicBoolean worldsRefreshed = new AtomicBoolean();
when(server.refreshWorlds()).thenAnswer(invocation -> {
worldsRefreshed.set(true);
return Thread.currentThread();
});
when(server.hasPlayerList()).thenReturn(true);
ModdedDimensionRegistryStore.PersistentDimension dimension =
new ModdedDimensionRegistryStore.PersistentDimension("iris:moon", "overworld", "overworld", 1337L);
try (MockedStatic<ModdedStartup> startup = mockStatic(ModdedStartup.class, CALLS_REAL_METHODS);
MockedStatic<ModdedDimensionRegistryStore> store = mockStatic(ModdedDimensionRegistryStore.class);
MockedStatic<ModdedDimensionManager> manager = mockStatic(ModdedDimensionManager.class);
MockedStatic<ModdedForcedDatapack> datapack = mockStatic(ModdedForcedDatapack.class);
MockedStatic<ModdedMixinAudit> audit = mockStatic(ModdedMixinAudit.class);
MockedStatic<ModdedIrisLog> log = mockStatic(ModdedIrisLog.class)) {
startup.when(ModdedStartup::prepareForStartup).thenAnswer(invocation -> null);
store.when(() -> ModdedDimensionRegistryStore.loadForStartup(server)).thenReturn(List.of(dimension));
manager.when(() -> ModdedDimensionManager.restorePersistent(server, "iris:moon", "overworld", "overworld", 1337L))
.thenAnswer(invocation -> {
assertTrue(worldsRefreshed.get());
return null;
});
ModdedLifecycleCallbacks.create().levelsReady().accept(server);
manager.verify(() -> ModdedDimensionManager.restorePersistent(server, "iris:moon", "overworld", "overworld", 1337L));
ModdedStartup.runOnce(server);
ModdedStartup.runOnce(server);
store.verify(() -> ModdedDimensionRegistryStore.loadForStartup(server), times(1));
manager.verify(() -> ModdedDimensionManager.restorePersistent(server, "iris:moon", "overworld", "overworld", 1337L), times(1));
datapack.verify(ModdedForcedDatapack::verifyInjected, times(1));
audit.verify(ModdedMixinAudit::runOnce, times(1));
}
}
}
@@ -3,14 +3,25 @@ package art.arcane.iris.modded.command;
import art.arcane.iris.configuration.IrisSettings;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.modded.ModdedGenPool;
import art.arcane.iris.world.pregen.MantleHeapPressure;
import art.arcane.iris.world.pregen.PregenListener;
import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime;
import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime.ChunkLoad;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -20,6 +31,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
@@ -31,6 +43,7 @@ import static org.mockito.Mockito.when;
public class ModdedPregenMethodLifecycleTest {
private MockedStatic<IrisSettings> settings;
private NativePregenRuntime runtime;
private Engine engine;
private ModdedPregenMethod method;
@Before
@@ -40,7 +53,7 @@ public class ModdedPregenMethodLifecycleTest {
settings.when(IrisSettings::get).thenReturn(configured);
runtime = mock(NativePregenRuntime.class);
when(runtime.worldIdentity()).thenReturn("example:terrain");
Engine engine = mock(Engine.class);
engine = mock(Engine.class, RETURNS_DEEP_STUBS);
when(engine.getHeight()).thenReturn(384);
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, true));
}
@@ -82,6 +95,131 @@ public class ModdedPregenMethodLifecycleTest {
verify(runtime, never()).save();
}
@Test
public void finalSaveWaitsForEveryCallbackBeyondFiveSeconds() throws Exception {
CompletableFuture<ChunkLoad> load = new CompletableFuture<>();
CompletableFuture<ChunkLoad> failedLoad = new CompletableFuture<>();
PregenListener listener = mock(PregenListener.class);
ExecutorService executor = Executors.newSingleThreadExecutor();
when(runtime.isRunning()).thenReturn(true);
when(runtime.loadChunk(2, 3)).thenReturn(load);
when(runtime.loadChunk(4, 5)).thenReturn(failedLoad);
doAnswer(call -> {
call.getArgument(0, Runnable.class).run();
return null;
}).when(runtime).execute(any());
try (MockedStatic<MantleHeapPressure> pressure = mockStatic(MantleHeapPressure.class)) {
pressure.when(MantleHeapPressure::memoryBudgetBytes).thenReturn(512L << 20);
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, false));
method.generateChunk(2, 3, listener);
method.generateChunk(4, 5, listener);
CountDownLatch started = new CountDownLatch(1);
Future<?> closing = executor.submit(() -> {
started.countDown();
method.close();
});
assertTrue(started.await(5, TimeUnit.SECONDS));
assertThrows(TimeoutException.class, () -> closing.get(6, TimeUnit.SECONDS));
verify(runtime, never()).save();
verify(engine, never()).syncGenerationHistory();
load.complete(new ChunkLoad(true, null));
assertThrows(TimeoutException.class, () -> closing.get(100, TimeUnit.MILLISECONDS));
verify(runtime, never()).save();
failedLoad.completeExceptionally(new IllegalStateException("chunk failed"));
closing.get(5, TimeUnit.SECONDS);
verify(listener).onChunkGenerated(2, 3);
verify(listener).onChunkFailed(4, 5);
verify(engine).syncGenerationHistory();
verify(runtime).save();
} finally {
load.complete(new ChunkLoad(true, null));
failedLoad.complete(new ChunkLoad(false, "chunk failed"));
executor.shutdownNow();
assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
}
}
@Test
public void interruptedCloseDrainsPendingChunksAndWaitsForFinalSave() throws Exception {
CompletableFuture<ChunkLoad> load = new CompletableFuture<>();
ExecutorService executor = Executors.newSingleThreadExecutor();
AtomicReference<Thread> closingThread = new AtomicReference<>();
AtomicReference<Runnable> queuedSave = new AtomicReference<>();
CountDownLatch started = new CountDownLatch(1);
CountDownLatch saveQueued = new CountDownLatch(1);
when(runtime.isRunning()).thenReturn(true);
when(runtime.loadChunk(2, 3)).thenReturn(load);
doAnswer(call -> {
queuedSave.set(call.getArgument(0, Runnable.class));
saveQueued.countDown();
return null;
}).when(runtime).execute(any());
try (MockedStatic<MantleHeapPressure> pressure = mockStatic(MantleHeapPressure.class)) {
pressure.when(MantleHeapPressure::memoryBudgetBytes).thenReturn(256L << 20);
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, false));
method.generateChunk(2, 3, mock(PregenListener.class));
Future<Boolean> closing = executor.submit(() -> {
closingThread.set(Thread.currentThread());
started.countDown();
method.close();
return Thread.currentThread().isInterrupted();
});
assertTrue(started.await(5, TimeUnit.SECONDS));
closingThread.get().interrupt();
assertThrows(TimeoutException.class, () -> closing.get(100, TimeUnit.MILLISECONDS));
verify(runtime, never()).save();
verify(engine, never()).syncGenerationHistory();
load.complete(new ChunkLoad(true, null));
assertTrue(saveQueued.await(5, TimeUnit.SECONDS));
assertThrows(TimeoutException.class, () -> closing.get(100, TimeUnit.MILLISECONDS));
verify(runtime, never()).save();
closingThread.get().interrupt();
assertThrows(TimeoutException.class, () -> closing.get(100, TimeUnit.MILLISECONDS));
queuedSave.get().run();
assertTrue(closing.get(5, TimeUnit.SECONDS));
verify(engine).syncGenerationHistory();
verify(runtime).save();
} finally {
load.complete(new ChunkLoad(true, null));
saveQueued.await(5, TimeUnit.SECONDS);
Runnable pendingSave = queuedSave.get();
if (pendingSave != null) {
pendingSave.run();
}
executor.shutdownNow();
assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
}
}
@Test
public void serverShutdownClaimsFinalSaveWithoutWaitingForPendingChunks() throws Exception {
CompletableFuture<ChunkLoad> load = new CompletableFuture<>();
PregenListener listener = mock(PregenListener.class);
ExecutorService executor = Executors.newSingleThreadExecutor();
Thread serverThread = Thread.currentThread();
when(runtime.isServerThread()).thenAnswer(call -> Thread.currentThread() == serverThread);
when(runtime.isRunning()).thenReturn(true);
when(runtime.loadChunk(2, 3)).thenReturn(load);
try (MockedStatic<MantleHeapPressure> pressure = mockStatic(MantleHeapPressure.class)) {
pressure.when(MantleHeapPressure::memoryBudgetBytes).thenReturn(256L << 20);
method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, false));
method.generateChunk(2, 3, listener);
Future<?> closing = executor.submit(method::close);
assertThrows(TimeoutException.class, () -> closing.get(100, TimeUnit.MILLISECONDS));
assertTrue(method.deferFinalSaveToServerThread());
closing.get(5, TimeUnit.SECONDS);
verify(runtime, never()).save();
assertTrue(method.hasPendingFinalSave());
method.completeDeferredFinalSave();
verify(runtime).save();
verify(runtime, never()).execute(any());
} finally {
load.complete(new ChunkLoad(true, null));
executor.shutdownNow();
assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
}
}
@Test
public void failedFinalSaveStillRestoresTheEmptyServerPauseSetting() {
AtomicInteger pause = new AtomicInteger(60);
@@ -8,6 +8,7 @@ import art.arcane.volmlib.nativelib.terrain.NativeWorld;
import com.mojang.serialization.JsonOps;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import org.junit.Test;
@@ -66,4 +67,26 @@ public class NativeGeneratorOwnershipTest {
assertNull(NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class));
assertFalse(nativeGenerator.represents(world));
}
@Test
public void protocolLookupTracksInstalledGeneratorAndPolicyOwner() {
IrisModdedChunkGenerator owner = new IrisModdedChunkGenerator(
new NativeGeneratorContext(mock(BiomeSource.class), "example:surface"));
NativeModdedChunkGenerator<?, ?, ?> nativeGenerator = (NativeModdedChunkGenerator<?, ?, ?>) owner.nativeGenerator();
ServerLevel level = mock(ServerLevel.class);
ServerChunkCache chunks = mock(ServerChunkCache.class);
when(level.dimension()).thenReturn(Level.OVERWORLD);
when(level.getChunkSource()).thenReturn(chunks);
when(chunks.getGenerator()).thenReturn(nativeGenerator);
NativeProtocolWorld world = new NativeProtocolWorld(level);
assertSame(owner, world.generator(IrisModdedChunkGenerator.class));
assertSame(nativeGenerator, world.generator(ChunkGenerator.class));
assertNull(world.generator(String.class));
ChunkGenerator replacement = mock(ChunkGenerator.class);
when(chunks.getGenerator()).thenReturn(replacement);
assertNull(world.generator(IrisModdedChunkGenerator.class));
assertSame(replacement, world.generator(ChunkGenerator.class));
}
}
@@ -0,0 +1,141 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.modded.NativeMixinFlags;
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeJigsawBounds;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeMixinTarget;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
/**
* Boot audit for the Iris mixin configs. Mixin registration is per-loader (fabric.mod.json entry, NeoForge
* mods.toml [[mixins]] block, Forge shadowJar MixinConfigs manifest attribute) and a config that never gets
* registered fails silently: no crash, the hooks simply never exist, and entity persistence, custom mob loot
* and the Iris world-type labels quietly stop working.
*
* <p>Application is checked structurally: Mixin transfers an {@code @Inject} handler into the target class,
* so the handler's presence on the target proves the mixin applied. Mixin 0.8.7 renames the transferred
* method to {@code handler$<ids>$<originalName>}, so the declared name is matched as an exact name or as a
* {@code $}-prefixed suffix. Client targets are resolved by name so this class stays free of client
* references and is safe on a dedicated server.
*/
public final class ModdedMixinAudit {
private static final String ENTITY_CONFIG = "volmlib.entity.mixins.json";
private static final String CLIENT_CONFIG = "volmlib.client.mixins.json";
private static final AtomicBoolean AUDITED = new AtomicBoolean(false);
static final List<ExpectedMixin> EXPECTED = List.of(
new ExpectedMixin(ENTITY_CONFIG, "ServerLevelsReadyMixin",
NativeMixinTarget.SERVER, "volmlib$levelsReady",
false, NativeMixinFlags::levelsReadyRan),
new ExpectedMixin(ENTITY_CONFIG, "JigsawPlacementBoundsMixin",
NativeMixinTarget.JIGSAW_PLACEMENT, "volmlib$verticalBounds",
false, NativeJigsawBounds::placementBoundsApplied),
new ExpectedMixin(ENTITY_CONFIG, "RegistryClientNamesMixin",
NativeMixinTarget.REGISTRY_PACKET, "volmlib$clientRegistryNames",
false, NativeMixinFlags::registryClientNamesRan),
new ExpectedMixin(ENTITY_CONFIG, "EntityPersistenceMixin",
NativeMixinTarget.ENTITY, "iris$applyGeneratedPersistence",
false, NativeMixinFlags::entityPersistenceRan),
new ExpectedMixin(ENTITY_CONFIG, "LivingEntityLootMixin",
NativeMixinTarget.LIVING_ENTITY, "iris$replaceBaseLoot",
false, NativeMixinFlags::livingEntityLootRan),
new ExpectedMixin(ENTITY_CONFIG, "MobAwarenessMixin",
NativeMixinTarget.MOB, "iris$tickUnawareMob",
false, NativeMixinFlags::mobAwarenessRan),
new ExpectedMixin(ENTITY_CONFIG, "StructureTemplatePaletteConcurrencyMixin",
NativeMixinTarget.STRUCTURE_PALETTE,
"iris$installConcurrentBlockCache",
false, NativeMixinFlags::structureTemplatePaletteRan),
new ExpectedMixin(ENTITY_CONFIG, "PackRepositorySelectionMixin",
NativeMixinTarget.PACK_REPOSITORY,
"volmlib$keepNativeDatapacksLast",
false, NativeMixinFlags::packSelectionRan),
new ExpectedMixin(CLIENT_CONFIG, "NativeWorldOpenFlowsMixin",
NativeMixinTarget.WORLD_OPEN_FLOWS,
"volmlib$openWorldCheckWorldStemCompatibility",
true, NativeMixinFlags::worldOpenFlowsRan),
new ExpectedMixin(CLIENT_CONFIG, "NativeWorldTypeEntryMixin",
NativeMixinTarget.WORLD_TYPE_ENTRY,
"volmlib$describePreset",
true, NativeMixinFlags::worldTypeEntryRan));
private ModdedMixinAudit() {
}
public static void runOnce() {
if (!AUDITED.compareAndSet(false, true)) {
return;
}
audit(ModdedEngineBootstrap.loader().platformName(),
ModdedEngineBootstrap.loader().clientEnvironment());
}
static void reset() {
AUDITED.set(false);
}
static void audit(String platform, boolean clientEnvironment) {
List<String> missing = new ArrayList<>();
List<String> applied = new ArrayList<>();
for (ExpectedMixin expected : EXPECTED) {
if (expected.clientOnly() && !clientEnvironment) {
continue;
}
if (isApplied(expected)) {
applied.add(expected.mixinName() + (expected.ran().getAsBoolean() ? "" : " (not yet exercised)"));
} else {
missing.add(expected.config() + '/' + expected.mixinName() + " -> " + expected.targetClass());
}
}
if (missing.isEmpty()) {
ModdedIrisLog.info("Iris mixin audit ok on {} ({} dist): {}", platform,
clientEnvironment ? "client" : "server", String.join(", ", applied));
return;
}
ModdedIrisLog.error("===============================================================");
ModdedIrisLog.error("Iris mixin audit FAILED on {} ({} dist): {} of {} expected mixin(s) were not applied.",
platform, clientEnvironment ? "client" : "server", missing.size(),
missing.size() + applied.size());
for (String entry : missing) {
ModdedIrisLog.error(" missing: {}", entry);
}
ModdedIrisLog.error("The mixin config was not registered for this loader (fabric.mod.json mixins, neoforge.mods.toml [[mixins]], forge MixinConfigs manifest attribute).");
ModdedIrisLog.error("Entity persistence, custom mob loot, parallel structure safety, Iris datapack priority, readable biome identifiers, or Iris world-type labels are disabled until this is fixed.");
ModdedIrisLog.error("===============================================================");
}
private static boolean isApplied(ExpectedMixin expected) {
try {
return expected.targetClass().hasInjectedHandler(expected.handlerMethod());
} catch (LinkageError unavailable) {
ModdedIrisLog.warn("Iris mixin audit could not inspect {}", expected.targetClass(), unavailable);
return false;
}
}
record ExpectedMixin(String config, String mixinName, NativeMixinTarget targetClass, String handlerMethod,
boolean clientOnly, BooleanSupplier ran) {
}
}
@@ -0,0 +1,7 @@
{
"pack": {
"description": "Iris client resources and mod data",
"pack_format": 34,
"supported_formats": [34, 48]
}
}
@@ -0,0 +1,145 @@
package art.arcane.iris.modded;
import net.minecraft.resources.ResourceKey;
import net.minecraft.core.registries.Registries;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.Registry;
import net.minecraft.core.MappedRegistry;
import com.mojang.serialization.Lifecycle;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeContainerLoot;
import art.arcane.volmlib.util.math.RNG;
import com.mojang.serialization.Codec;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMapper;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.Bootstrap;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.Container;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import net.minecraft.world.level.biome.MobSpawnSettings;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class IrisModdedBlockEntityParityTest {
private static RegistryAccess registries;
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
registries = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY);
}
@Test
public void generatedContainerGetsBlockEntityAndPersistsFilledLoot() {
ProtoChunk chunk = newChunk();
BlockPos position = new BlockPos(3, 70, 5);
chunk.setBlockState(position, Blocks.CHEST.defaultBlockState(), false);
NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, position, Blocks.CHEST.defaultBlockState());
BlockEntity blockEntity = chunk.getBlockEntity(position);
assertNotNull(blockEntity);
assertTrue(blockEntity instanceof Container);
Container container = (Container) blockEntity;
NativeContainerLoot.fillContainer(container, List.of(new ItemStack(Items.DIAMOND)), new RNG(17L), message -> {});
assertEquals(1, countItem(container, Items.DIAMOND));
CompoundTag saved = blockEntity.saveWithoutMetadata(registries);
ChestBlockEntity restored = new ChestBlockEntity(position, Blocks.CHEST.defaultBlockState());
restored.loadWithComponents(saved, registries);
assertEquals(1, countItem(restored, Items.DIAMOND));
}
@Test
public void generatedSignSpawnerAndBannerGetNativeBlockEntities() {
ProtoChunk chunk = newChunk();
BlockState signState = Blocks.OAK_SIGN.defaultBlockState();
BlockState spawnerState = Blocks.SPAWNER.defaultBlockState();
BlockState bannerState = BuiltInRegistries.BLOCK.get(
ResourceLocation.parse("minecraft:white_banner")).defaultBlockState();
BlockPos signPosition = new BlockPos(1, 70, 1);
BlockPos spawnerPosition = new BlockPos(2, 70, 1);
BlockPos bannerPosition = new BlockPos(3, 70, 1);
chunk.setBlockState(signPosition, signState, false);
chunk.setBlockState(spawnerPosition, spawnerState, false);
chunk.setBlockState(bannerPosition, bannerState, false);
NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, signPosition, signState);
NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, spawnerPosition, spawnerState);
NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, bannerPosition, bannerState);
assertTrue(chunk.getBlockEntity(signPosition) instanceof SignBlockEntity);
assertTrue(chunk.getBlockEntity(spawnerPosition) instanceof SpawnerBlockEntity);
assertTrue(chunk.getBlockEntity(bannerPosition) instanceof BannerBlockEntity);
}
private static ProtoChunk newChunk() {
return new ProtoChunk(
new ChunkPos(0, 0),
UpgradeData.EMPTY,
LevelHeightAccessor.create(-64, 384),
biomeRegistry(),
null);
}
private static Registry<Biome> biomeRegistry() {
Biome biome = new Biome.BiomeBuilder()
.hasPrecipitation(false)
.temperature(0.8F)
.downfall(0.4F)
.specialEffects(new BiomeSpecialEffects.Builder().fogColor(0).waterColor(0x3F76E4)
.waterFogColor(0).skyColor(0).build())
.mobSpawnSettings(MobSpawnSettings.EMPTY)
.generationSettings(BiomeGenerationSettings.EMPTY)
.build();
MappedRegistry<Biome> registry = new MappedRegistry<>(Registries.BIOME, Lifecycle.stable());
registry.register(ResourceKey.create(Registries.BIOME, ResourceLocation.withDefaultNamespace("plains")),
biome, RegistrationInfo.BUILT_IN);
return registry.freeze();
}
private static int countItem(Container container, Item item) {
int count = 0;
for (int slot = 0; slot < container.getContainerSize(); slot++) {
ItemStack stack = container.getItem(slot);
if (stack.getItem() == item) {
count += stack.getCount();
}
}
return count;
}
}
@@ -0,0 +1,48 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnTableMerger;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.MobSpawnSettings;
import org.junit.Test;
import org.junit.BeforeClass;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class IrisModdedChunkGeneratorSpawnTest {
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void explicitSpawnsReplaceMatchingVanillaTypesAndPreserveOthers() {
MobSpawnSettings.SpawnerData zombie = new MobSpawnSettings.SpawnerData(EntityType.ZOMBIE, 100, 1, 4);
MobSpawnSettings.SpawnerData vanillaSlime = new MobSpawnSettings.SpawnerData(EntityType.SLIME, 1, 1, 1);
MobSpawnSettings.SpawnerData explicitSlime = new MobSpawnSettings.SpawnerData(EntityType.SLIME, 7, 2, 5);
MobSpawnSettings.SpawnerData explicitCow = new MobSpawnSettings.SpawnerData(EntityType.COW, 3, 2, 4);
WeightedRandomList<MobSpawnSettings.SpawnerData> vanilla = WeightedRandomList.create(List.of(
zombie,
vanillaSlime));
WeightedRandomList<MobSpawnSettings.SpawnerData> explicit = WeightedRandomList.create(List.of(
explicitSlime,
explicitCow));
WeightedRandomList<MobSpawnSettings.SpawnerData> merged = NativeSpawnTableMerger.merge(vanilla, explicit);
List<MobSpawnSettings.SpawnerData> entries = merged.unwrap();
assertEquals(3, entries.size());
assertEquals(EntityType.ZOMBIE, entries.get(0).type);
assertEquals(100, entries.get(0).getWeight().asInt());
assertEquals(explicitSlime, entries.get(1));
assertEquals(7, entries.get(1).getWeight().asInt());
assertEquals(explicitCow, entries.get(2));
assertEquals(3, entries.get(2).getWeight().asInt());
}
}
@@ -0,0 +1,134 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntitySpawns;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.world.entity.IrisEntity;
import art.arcane.volmlib.util.math.RNG;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.chunk.LevelChunk;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ModdedEntitySpawnDifficultyTest {
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void sharedPlatformSpawnReturnsFalseForPeacefulHostiles() {
ServerLevel level = mock(ServerLevel.class);
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
ModdedPlatform platform = new ModdedPlatform(mock(NativeModdedLoader.class));
assertFalse(platform.spawnEntity(new ModdedPlatformWorld(level), "minecraft:zombie", 0.5, 80, 0.5));
verify(level, never()).addFreshEntity(any(Entity.class));
verify(level, never()).enabledFeatures();
}
@Test
public void ambientSpawnReturnsNullForPeacefulHostilesBeforeApplyingConfiguration() {
ServerLevel level = mock(ServerLevel.class);
ServerChunkCache chunks = mock(ServerChunkCache.class);
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
when(level.getChunkSource()).thenReturn(chunks);
when(chunks.getChunkNow(anyInt(), anyInt())).thenReturn(mock(LevelChunk.class));
IrisEntity configured = new IrisEntity().setType("zombie").setSpawnEffectRiseOutOfGround(false);
assertNull(ModdedEntitySpawner.spawn(mock(Engine.class), configured, new NativeEntityRuntime(level), 0, 80, 0, new RNG(1L)));
verify(level, never()).addFreshEntity(any(Entity.class));
verify(level, never()).enabledFeatures();
}
@Test
public void peacefulRejectsNativeHostilesBeforeCreation() {
ServerLevel level = mock(ServerLevel.class);
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
for (EntityType<?> type : List.of(EntityType.ZOMBIE, EntityType.SKELETON, EntityType.CREEPER,
EntityType.SPIDER, EntityType.ENDERMAN, EntityType.BLAZE, EntityType.GHAST,
EntityType.PHANTOM, EntityType.SLIME, EntityType.MAGMA_CUBE, EntityType.WITHER,
EntityType.PIGLIN_BRUTE, EntityType.ZOGLIN, EntityType.WARDEN)) {
assertNull(NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, MobSpawnType.NATURAL));
}
verify(level, never()).enabledFeatures();
verify(level, never()).addFreshEntityWithPassengers(any(Entity.class));
}
@Test
public void peacefulPreservesPassiveAndNativeMonsterCategoryExceptions() {
ServerLevel level = mock(ServerLevel.class);
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
IllegalStateException reachedNativeFactory = new IllegalStateException("Native entity factory reached");
when(level.enabledFeatures()).thenThrow(reachedNativeFactory);
for (EntityType<?> type : List.of(EntityType.COW, EntityType.VILLAGER, EntityType.WOLF,
EntityType.PIGLIN, EntityType.HOGLIN, EntityType.SHULKER, EntityType.ENDER_DRAGON)) {
assertSame(reachedNativeFactory, assertThrows(IllegalStateException.class,
() -> NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, MobSpawnType.NATURAL)));
}
}
@Test
public void nonPeacefulDifficultiesPreserveHostileSpawns() {
ServerLevel level = mock(ServerLevel.class);
Entity created = mock(Entity.class);
for (Difficulty difficulty : List.of(Difficulty.EASY, Difficulty.NORMAL, Difficulty.HARD)) {
EntityType<?> type = spy(BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.parse("minecraft:zombie")));
when(level.getDifficulty()).thenReturn(difficulty);
doReturn(created).when(type).spawn(level, BlockPos.ZERO, MobSpawnType.NATURAL);
assertSame(created, NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, MobSpawnType.NATURAL));
verify(type).spawn(level, BlockPos.ZERO, MobSpawnType.NATURAL);
}
}
@Test
public void unrelatedNativeSpawnFailuresStillPropagate() {
ServerLevel level = mock(ServerLevel.class);
when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL);
EntityType<?> type = spy(BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.parse("minecraft:cow")));
IllegalStateException failure = new IllegalStateException("Spawn rejected by platform");
doThrow(failure).when(type).spawn(level, BlockPos.ZERO, MobSpawnType.NATURAL);
assertSame(failure, assertThrows(IllegalStateException.class,
() -> NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, MobSpawnType.NATURAL)));
}
}
@@ -0,0 +1,167 @@
package art.arcane.iris.modded;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.registries.Registries;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.Registry;
import net.minecraft.core.MappedRegistry;
import com.mojang.serialization.Lifecycle;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedStructureStage;
import com.mojang.serialization.Codec;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.IdMapper;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.entity.ai.village.poi.PoiSection;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.entity.ai.village.poi.PoiType;
import net.minecraft.world.entity.ai.village.poi.PoiTypes;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import net.minecraft.world.level.biome.MobSpawnSettings;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ModdedNativeStructurePoiRegistrationTest {
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void visitsOnlyExistingPoiBlocksBeforeNativePlacement() {
ProtoChunk chunk = newChunk();
BlockPos poiPosition = new BlockPos(3, 70, 5);
BlockPos ordinaryPosition = new BlockPos(4, 70, 5);
BlockState poiState = Blocks.BARREL.defaultBlockState();
chunk.setBlockState(poiPosition, poiState, false);
chunk.setBlockState(ordinaryPosition, Blocks.STONE.defaultBlockState(), false);
Map<BlockPos, BlockState> visited = new LinkedHashMap<>();
NativeModdedStructureStage.visitExistingPois(
chunk, (position, state) -> visited.put(position.immutable(), state));
assertEquals(poiState, visited.get(poiPosition));
assertFalse(visited.containsKey(ordinaryPosition));
assertEquals(1, visited.size());
}
@Test
public void duplicatePrimingIsIdempotentForAnAlreadyRegisteredPoi() {
BlockPos position = new BlockPos(3, 70, 5);
Holder<PoiType> type = PoiTypes.forState(
Blocks.BARREL.defaultBlockState()).orElseThrow();
PoiSection section = new PoiSection(() -> { });
section.add(position, type);
section.add(position, type);
assertEquals(1L, section.getRecords(candidate -> true, PoiManager.Occupancy.ANY).count());
assertEquals(type, section.getType(position).orElseThrow());
section.remove(position);
assertTrue(section.getType(position).isEmpty());
}
@Test
public void worldgenPrimingQueuesBeforeTheLaterVanillaRemoval() {
ProtoChunk chunk = newChunk();
BlockPos position = new BlockPos(3, 70, 5);
BlockState poiState = Blocks.BARREL.defaultBlockState();
chunk.setBlockState(position, poiState, false);
PoiSection section = new PoiSection(() -> { });
Deque<Runnable> serverQueue = new ArrayDeque<>();
AtomicInteger missingRemovals = new AtomicInteger();
NativeModdedStructureStage.visitExistingPois(chunk,
(poiPosition, state) -> queuePoiTransition(
serverQueue, section, poiPosition,
Blocks.AIR.defaultBlockState(), state, missingRemovals));
assertTrue(section.getType(position).isEmpty());
queuePoiTransition(serverQueue, section, position,
poiState, Blocks.AIR.defaultBlockState(), missingRemovals);
while (!serverQueue.isEmpty()) {
serverQueue.removeFirst().run();
}
assertEquals(0, missingRemovals.get());
assertTrue(section.getType(position).isEmpty());
}
private static void queuePoiTransition(Deque<Runnable> serverQueue,
PoiSection section,
BlockPos position,
BlockState oldState,
BlockState newState,
AtomicInteger missingRemovals) {
Optional<Holder<PoiType>> oldType = PoiTypes.forState(oldState);
Optional<Holder<PoiType>> newType = PoiTypes.forState(newState);
if (Objects.equals(oldType, newType)) {
return;
}
BlockPos immutablePosition = position.immutable();
oldType.ifPresent(type -> serverQueue.addLast(() -> {
if (section.getType(immutablePosition).isEmpty()) {
missingRemovals.incrementAndGet();
return;
}
section.remove(immutablePosition);
}));
newType.ifPresent(type -> serverQueue.addLast(
() -> section.add(immutablePosition, type)));
}
private static ProtoChunk newChunk() {
return new ProtoChunk(
new ChunkPos(0, 0),
UpgradeData.EMPTY,
LevelHeightAccessor.create(-64, 384),
biomeRegistry(),
null);
}
private static Registry<Biome> biomeRegistry() {
Biome biome = new Biome.BiomeBuilder()
.hasPrecipitation(false)
.temperature(0.8F)
.downfall(0.4F)
.specialEffects(new BiomeSpecialEffects.Builder().fogColor(0).waterColor(0x3F76E4)
.waterFogColor(0).skyColor(0).build())
.mobSpawnSettings(MobSpawnSettings.EMPTY)
.generationSettings(BiomeGenerationSettings.EMPTY)
.build();
MappedRegistry<Biome> registry = new MappedRegistry<>(Registries.BIOME, Lifecycle.stable());
registry.register(ResourceKey.create(Registries.BIOME, ResourceLocation.withDefaultNamespace("plains")),
biome, RegistrationInfo.BUILT_IN);
return registry.freeze();
}
}
@@ -0,0 +1,45 @@
package art.arcane.iris.modded;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.metadata.pack.PackMetadataSection;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class ModdedPackMetadataTest {
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void metadataLoadsClientResourcesAndServerDataForCurrentMinecraft() throws IOException {
try (InputStream resource = ModdedPackMetadataTest.class.getResourceAsStream("/pack.mcmeta")) {
assertNotNull(resource);
try (InputStreamReader reader = new InputStreamReader(resource, StandardCharsets.UTF_8)) {
JsonObject metadata = JsonParser.parseReader(reader).getAsJsonObject();
for (PackType type : PackType.values()) {
PackMetadataSection section = PackMetadataSection.CODEC
.parse(JsonOps.INSTANCE, metadata.get("pack")).getOrThrow();
assertEquals("Iris client resources and mod data", section.description().getString());
assertTrue(type.toString(), section.supportedFormats().orElseThrow().isValueInRange(
SharedConstants.getCurrentVersion().getPackVersion(type)));
}
}
}
}
}
@@ -0,0 +1,253 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureSetFrequencyOverrides;
import art.arcane.iris.structure.nativegen.IrisImportedStructureControl;
import art.arcane.iris.structure.placement.IrisStructureSetFrequencyOverride;
import art.arcane.volmlib.util.collection.KList;
import net.minecraft.SharedConstants;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderOwner;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Vec3i;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacementType;
import com.mojang.serialization.MapCodec;
import java.util.stream.Stream;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
public class ModdedStructureSetFrequencyOverridesTest {
@BeforeClass
public static void bootstrapMinecraftRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void createStateReplacementScalesOnlyTheExactSetAndRetainsEntries() throws Exception {
Holder<StructureSet> complexes = structureSet(
"minecraft:nether_complexes", 27, 4, 30084232);
Holder<StructureSet> fossils = structureSet(
"minecraft:nether_fossils", 2, 1, 14357921);
ChunkGeneratorStructureState state = state(List.of(complexes, fossils));
KList<IrisStructureSetFrequencyOverride> overrides = new KList<>();
overrides.add(new IrisStructureSetFrequencyOverride()
.setStructureSet("minecraft:nether_complexes")
.setMultiplier(1.1D));
IrisImportedStructureControl control = new IrisImportedStructureControl()
.setFrequencyOverrides(overrides);
ChunkGeneratorStructureState returned =
NativeStructureSetFrequencyOverrides.apply(state, control);
assertSame(state, returned);
List<Holder<StructureSet>> scaledSets = state.possibleStructureSets();
RandomSpreadStructurePlacement scaledComplexes =
(RandomSpreadStructurePlacement) scaledSets.get(0).value().placement();
assertEquals(26, scaledComplexes.spacing());
assertEquals(4, scaledComplexes.separation());
assertSame(complexes.value().structures(), scaledSets.get(0).value().structures());
assertSame(fossils, scaledSets.get(1));
}
@Test
public void unrelatedCustomPlacementTypeRemainsUntouched() {
Holder<StructureSet> custom = new BoundStructureSetHolder(
"example:custom",
new StructureSet(List.of(), new UnsupportedPlacement()));
KList<IrisStructureSetFrequencyOverride> overrides = new KList<>();
overrides.add(new IrisStructureSetFrequencyOverride()
.setStructureSet("minecraft:nether_complexes")
.setMultiplier(1.1D));
IrisImportedStructureControl control = new IrisImportedStructureControl()
.setFrequencyOverrides(overrides);
List<Holder<StructureSet>> scaled =
NativeStructureSetFrequencyOverrides.scaleSets(List.of(custom), control);
assertSame(custom, scaled.getFirst());
}
@Test
public void unrelatedExclusionCycleRemainsUntouched() {
BoundStructureSetHolder first = new BoundStructureSetHolder("example:first");
BoundStructureSetHolder second = new BoundStructureSetHolder("example:second");
first.bind(new StructureSet(List.of(), placement(32, 8, 1, second)));
second.bind(new StructureSet(List.of(), placement(40, 10, 2, first)));
KList<IrisStructureSetFrequencyOverride> overrides = new KList<>();
overrides.add(new IrisStructureSetFrequencyOverride()
.setStructureSet("minecraft:nether_complexes")
.setMultiplier(1.1D));
IrisImportedStructureControl control = new IrisImportedStructureControl()
.setFrequencyOverrides(overrides);
List<Holder<StructureSet>> scaled = NativeStructureSetFrequencyOverrides.scaleSets(
List.of(first, second), control);
assertSame(first, scaled.get(0));
assertSame(second, scaled.get(1));
}
@Test
public void affectedExclusionCycleUsesBoundScaledHolders() {
BoundStructureSetHolder first = new BoundStructureSetHolder("example:first");
BoundStructureSetHolder second = new BoundStructureSetHolder("example:second");
first.bind(new StructureSet(List.of(), placement(32, 8, 1, second)));
second.bind(new StructureSet(List.of(), placement(40, 10, 2, first)));
KList<IrisStructureSetFrequencyOverride> overrides = new KList<>();
overrides.add(new IrisStructureSetFrequencyOverride()
.setStructureSet("example:first")
.setMultiplier(1.1D));
IrisImportedStructureControl control = new IrisImportedStructureControl()
.setFrequencyOverrides(overrides);
List<Holder<StructureSet>> scaled = NativeStructureSetFrequencyOverrides.scaleSets(
List.of(first, second), control);
assertEquals(31, ((RandomSpreadStructurePlacement)
scaled.get(0).value().placement()).spacing());
assertEquals(40, ((RandomSpreadStructurePlacement)
scaled.get(1).value().placement()).spacing());
}
@Test
public void affectedRandomSpreadSubclassFailsInsteadOfLosingSubtypeBehavior() {
Holder<StructureSet> custom = new BoundStructureSetHolder(
"example:custom",
new StructureSet(List.of(), new CustomRandomSpreadPlacement()));
KList<IrisStructureSetFrequencyOverride> overrides = new KList<>();
overrides.add(new IrisStructureSetFrequencyOverride()
.setStructureSet("example:custom")
.setMultiplier(1.1D));
IrisImportedStructureControl control = new IrisImportedStructureControl()
.setFrequencyOverrides(overrides);
assertThrows(IllegalStateException.class, () ->
NativeStructureSetFrequencyOverrides.scaleSets(List.of(custom), control));
}
private static Holder<StructureSet> structureSet(
String key,
int spacing,
int separation,
int salt
) {
StructureSet value = new StructureSet(
List.of(),
new RandomSpreadStructurePlacement(
spacing, separation, RandomSpreadType.LINEAR, salt));
return new BoundStructureSetHolder(key, value);
}
private static RandomSpreadStructurePlacement placement(
int spacing,
int separation,
int salt,
Holder<StructureSet> exclusionTarget
) {
return new RandomSpreadStructurePlacement(
Vec3i.ZERO,
StructurePlacement.FrequencyReductionMethod.DEFAULT,
1F,
salt,
Optional.of(new StructurePlacement.ExclusionZone(exclusionTarget, 1)),
spacing,
separation,
RandomSpreadType.LINEAR);
}
private static ChunkGeneratorStructureState state(
List<Holder<StructureSet>> sets
) throws Exception {
Constructor<ChunkGeneratorStructureState> constructor =
ChunkGeneratorStructureState.class.getDeclaredConstructor(
RandomState.class,
BiomeSource.class,
long.class,
long.class,
List.class);
constructor.setAccessible(true);
return constructor.newInstance(
null,
new EmptyBiomeSource(),
1L,
1L,
sets);
}
private static final class BoundStructureSetHolder extends Holder.Reference<StructureSet> {
private BoundStructureSetHolder(String key) {
this(key, null);
}
private BoundStructureSetHolder(String key, StructureSet value) {
super(Type.STAND_ALONE, new HolderOwner<>() {
}, ResourceKey.create(Registries.STRUCTURE_SET, ResourceLocation.parse(key)), value);
}
private void bind(StructureSet value) {
bindValue(value);
}
}
private static final class EmptyBiomeSource extends BiomeSource {
@Override
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
return null;
}
@Override
protected MapCodec<? extends BiomeSource> codec() {
throw new UnsupportedOperationException();
}
@Override
protected Stream<Holder<Biome>> collectPossibleBiomes() {
return HolderSet.<Biome>empty().stream();
}
}
private static final class UnsupportedPlacement extends StructurePlacement {
private UnsupportedPlacement() {
super(Vec3i.ZERO, FrequencyReductionMethod.DEFAULT, 1F, 0, Optional.empty());
}
@Override
protected boolean isPlacementChunk(ChunkGeneratorStructureState state, int x, int z) {
return false;
}
@Override
public StructurePlacementType<?> type() {
return null;
}
}
private static final class CustomRandomSpreadPlacement extends RandomSpreadStructurePlacement {
private CustomRandomSpreadPlacement() {
super(27, 4, RandomSpreadType.LINEAR, 30084232);
}
}
}
@@ -0,0 +1,71 @@
package art.arcane.volmlib.nativelib.minecraft26_2.modded;
import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.concurrent.CompletableFuture;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
public class NativePregenRuntimeTest {
@BeforeClass
public static void bootstrapRegistries() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void chunkTicketAdmissionAndReleaseRunThroughTheServerExecutor() {
MinecraftServer server = mock(MinecraftServer.class);
ServerLevel level = mock(ServerLevel.class);
ServerChunkCache chunks = mock(ServerChunkCache.class);
when(level.getServer()).thenReturn(server);
when(level.getChunkSource()).thenReturn(chunks);
CompletableFuture<ChunkResult<ChunkAccess>> loaded = new CompletableFuture<>();
doReturn(loaded).when(chunks).getChunkFutureMainThread(-2, 7, ChunkStatus.FULL, true);
NativePregenRuntime runtime = NativeModdedPregenRuntime.from(new ModdedPlatformWorld(level));
CompletableFuture<NativePregenRuntime.ChunkLoad> result = runtime.loadChunk(-2, 7);
verify(chunks, never()).addRegionTicket(any(), any(), eq(0), any());
verify(chunks, never()).getChunkFutureMainThread(-2, 7, ChunkStatus.FULL, true);
ArgumentCaptor<Runnable> requests = ArgumentCaptor.forClass(Runnable.class);
verify(server).execute(requests.capture());
requests.getValue().run();
assertFalse(result.isDone());
verify(chunks).getChunkFutureMainThread(-2, 7, ChunkStatus.FULL, true);
loaded.complete(ChunkResult.of(mock(ChunkAccess.class)));
assertTrue(result.join().successful());
ArgumentCaptor<TicketType> ticket = ArgumentCaptor.forClass(TicketType.class);
verify(chunks).addRegionTicket(ticket.capture(), eq(new ChunkPos(-2, 7)), eq(0), eq(new ChunkPos(-2, 7)));
runtime.releaseChunk(-2, 7);
verify(chunks, never()).removeRegionTicket(any(), any(), eq(0), any());
verify(server, times(2)).execute(requests.capture());
requests.getAllValues().getLast().run();
ArgumentCaptor<TicketType> released = ArgumentCaptor.forClass(TicketType.class);
verify(chunks).removeRegionTicket(released.capture(), eq(new ChunkPos(-2, 7)), eq(0), eq(new ChunkPos(-2, 7)));
assertSame(ticket.getValue(), released.getValue());
}
}
@@ -0,0 +1,130 @@
/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.volmlib.nativelib.minecraft26_2.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenBiome;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenPlacedFeature;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.Lifecycle;
import net.minecraft.SharedConstants;
import net.minecraft.core.Holder;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NativeWorldgenScanTest {
private static final String BIOME_JSON = "{\"has_precipitation\":true,\"temperature\":0.8,\"downfall\":0.4,"
+ "\"effects\":{\"fog_color\":12638463,\"water_color\":4159204,\"water_fog_color\":329011,\"sky_color\":7907327},"
+ "\"carvers\":{},\"features\":[[],[],[],[],[],[],[\"examplemod:ore_glimmer\"]],"
+ "\"spawners\":{},\"spawn_costs\":{}}";
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void scanReadsLiveFeatureStepsAndEncodesDefinitions() {
NativeWorldgenSnapshot snapshot = NativeWorldgenScan.scan(server(), loader()).encode();
WorldgenBiome biome = snapshot.biomes().getFirst();
assertEquals("examplemod:glade", biome.key());
assertEquals(0.8F, biome.temperature(), 0F);
assertEquals(0.4F, biome.downfall(), 0.0001F);
assertTrue(biome.hasPrecipitation());
assertEquals(GenerationStep.Decoration.values().length, biome.featureSteps().size());
assertEquals(List.of("examplemod:ore_glimmer"),
biome.featureSteps().get(GenerationStep.Decoration.UNDERGROUND_ORES.ordinal()));
assertNotNull(biome.definition());
assertEquals(0.4F, biome.definition().getAsJsonObject().get("downfall").getAsFloat(), 0.0001F);
}
@Test
public void unencodableFeaturesStayInTheCatalogWithoutJson() {
NativeWorldgenSnapshot snapshot = NativeWorldgenScan.scan(server(), loader()).encode();
WorldgenPlacedFeature feature = snapshot.placedFeatures().getFirst();
assertEquals("examplemod:ore_glimmer", feature.key());
assertEquals("", feature.feature());
assertNull(feature.config());
assertTrue(feature.placement().isJsonArray());
assertEquals(1, snapshot.failures().count());
assertTrue(snapshot.failures().samples().getFirst().startsWith("feature of examplemod:ore_glimmer"));
assertTrue(snapshot.levelStems().isEmpty());
}
private static NativeModdedServer server() {
MappedRegistry<PlacedFeature> features = new MappedRegistry<>(Registries.PLACED_FEATURE, Lifecycle.stable());
features.register(ResourceKey.create(Registries.PLACED_FEATURE, ResourceLocation.parse("examplemod:ore_glimmer")),
new PlacedFeature(Holder.direct(mock(ConfiguredFeature.class)), List.of()), RegistrationInfo.BUILT_IN);
features.freeze();
MappedRegistry<ConfiguredWorldCarver<?>> carvers = new MappedRegistry<>(Registries.CONFIGURED_CARVER, Lifecycle.stable());
carvers.freeze();
MappedRegistry<Biome> biomes = new MappedRegistry<>(Registries.BIOME, Lifecycle.stable());
MappedRegistry<Structure> structures = new MappedRegistry<>(Registries.STRUCTURE, Lifecycle.stable());
structures.freeze();
MappedRegistry<StructureSet> structureSets = new MappedRegistry<>(Registries.STRUCTURE_SET, Lifecycle.stable());
structureSets.freeze();
MappedRegistry<DimensionType> dimensionTypes = new MappedRegistry<>(Registries.DIMENSION_TYPE, Lifecycle.stable());
dimensionTypes.freeze();
RegistryAccess access = new RegistryAccess.ImmutableRegistryAccess(
List.of(biomes, features, carvers, structures, structureSets, dimensionTypes));
Biome biome = Biome.DIRECT_CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, access),
JsonParser.parseString(BIOME_JSON)).getOrThrow();
biomes.register(ResourceKey.create(Registries.BIOME, ResourceLocation.parse("examplemod:glade")), biome, RegistrationInfo.BUILT_IN);
biomes.freeze();
MinecraftServer server = mock(MinecraftServer.class);
when(server.registryAccess()).thenReturn(access.freeze());
return NativeModdedServer.fromHandle(server);
}
private static NativeModdedLoader loader() {
NativeModdedLoader loader = mock(NativeModdedLoader.class);
when(loader.unmodifiedBiome(any())).thenAnswer(invocation -> invocation.getArgument(0));
return loader;
}
}
@@ -0,0 +1,68 @@
package art.arcane.volmlib.nativelib.minecraft26_2.terrain;
import org.junit.Test;
import java.util.OptionalInt;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
public class NativeJigsawBoundsTest {
@Test
public void verticalDistanceUsesInclusiveBlocksWithinExclusiveHeightLimits() {
try (NativeJigsawBounds bounds = NativeJigsawBounds.open(OptionalInt.of(12))) {
NativeJigsawBounds.heightLimits(-64, 320);
NativeJigsawBounds.center(100);
assertEquals(88, NativeJigsawBounds.minimum(40), 0);
assertEquals(113, NativeJigsawBounds.maximum(160), 0);
NativeJigsawBounds.center(-60);
assertEquals(-64, NativeJigsawBounds.minimum(-120), 0);
assertEquals(-47, NativeJigsawBounds.maximum(0), 0);
NativeJigsawBounds.center(315);
assertEquals(303, NativeJigsawBounds.minimum(255), 0);
assertEquals(320, NativeJigsawBounds.maximum(375), 0);
}
}
@Test
public void nestedScopesRestoreTheOuterDistanceAndCenter() {
try (NativeJigsawBounds outer = NativeJigsawBounds.open(OptionalInt.of(10))) {
NativeJigsawBounds.heightLimits(-64, 320);
NativeJigsawBounds.center(80);
try (NativeJigsawBounds inner = NativeJigsawBounds.open(OptionalInt.of(2))) {
NativeJigsawBounds.heightLimits(0, 256);
NativeJigsawBounds.center(200);
assertEquals(198, NativeJigsawBounds.minimum(0), 0);
assertEquals(203, NativeJigsawBounds.maximum(256), 0);
}
assertEquals(70, NativeJigsawBounds.minimum(0), 0);
assertEquals(91, NativeJigsawBounds.maximum(256), 0);
try (NativeJigsawBounds unbounded = NativeJigsawBounds.open(OptionalInt.empty())) {
assertEquals(-20, NativeJigsawBounds.minimum(-20), 0);
assertEquals(400, NativeJigsawBounds.maximum(400), 0);
}
assertEquals(70, NativeJigsawBounds.minimum(0), 0);
}
assertEquals(-20, NativeJigsawBounds.minimum(-20), 0);
assertEquals(400, NativeJigsawBounds.maximum(400), 0);
}
@Test
public void failedGenerationClosesTheScopeAndRejectsNegativeDistance() {
assertThrows(IllegalStateException.class, () -> {
try (NativeJigsawBounds bounds = NativeJigsawBounds.open(OptionalInt.of(0))) {
NativeJigsawBounds.heightLimits(-64, 320);
NativeJigsawBounds.center(40);
assertEquals(40, NativeJigsawBounds.minimum(-100), 0);
assertEquals(41, NativeJigsawBounds.maximum(100), 0);
throw new IllegalStateException("generation failed");
}
});
assertEquals(-100, NativeJigsawBounds.minimum(-100), 0);
assertEquals(100, NativeJigsawBounds.maximum(100), 0);
assertThrows(IllegalArgumentException.class, () -> NativeJigsawBounds.open(OptionalInt.of(-1)));
assertEquals(-100, NativeJigsawBounds.minimum(-100), 0);
}
}
@@ -0,0 +1,41 @@
package art.arcane.volmlib.nativelib.minecraft26_2.terrain;
import com.mojang.datafixers.util.Either;
import net.minecraft.SharedConstants;
import net.minecraft.core.Holder;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.assertSame;
public class NativeStructureTemplateAccessTest {
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void resolvesRuntimeTemplateThroughWidenedPoolField() {
StructureTemplate template = new StructureTemplate();
SinglePoolElement element = new RuntimePoolElement(template);
assertSame(template, NativeStructureReflection.resolveTemplate(element, () -> {
throw new AssertionError("Runtime templates must not require a template manager");
}));
}
private static final class RuntimePoolElement extends SinglePoolElement {
private RuntimePoolElement(StructureTemplate template) {
super(Either.right(template), Holder.direct(new StructureProcessorList(List.of())),
StructureTemplatePool.Projection.RIGID, Optional.empty());
}
}
}
@@ -0,0 +1,430 @@
{
"global": [
{
"pattern": "\\bIdentifier\\b",
"replacement": "ResourceLocation"
},
{
"pattern": "\\bEntitySpawnReason\\b",
"replacement": "MobSpawnType"
},
{
"pattern": "net\\.minecraft\\.world\\.entity\\.animal\\.panda\\.Panda",
"replacement": "net.minecraft.world.entity.animal.Panda"
},
{
"pattern": "net\\.minecraft\\.world\\.entity\\.npc\\.villager\\.Villager",
"replacement": "net.minecraft.world.entity.npc.Villager"
},
{
"pattern": "net\\.minecraft\\.world\\.level\\.gamerules\\.GameRules",
"replacement": "net.minecraft.world.level.GameRules"
},
{
"pattern": "\\bSpeleothemBlock\\b",
"replacement": "PointedDripstoneBlock"
},
{
"pattern": "\\bSpeleothemThickness\\b",
"replacement": "DripstoneThickness"
},
{
"pattern": "\\bEntityTypes\\b",
"replacement": "EntityType"
},
{
"pattern": "\\bAbstractSpreadingStructurePlacement\\b",
"replacement": "StructurePlacement"
},
{
"pattern": "\\bDirectPoolAlias\\b",
"replacement": "Direct"
},
{
"pattern": "\\bRandomGroupPoolAlias\\b",
"replacement": "RandomGroup"
},
{
"pattern": "\\bRandomPoolAlias\\b",
"replacement": "Random"
},
{
"pattern": "\\.identifier\\(\\)",
"replacement": ".location()"
},
{
"pattern": "ChunkPos\\.pack\\(",
"replacement": "ChunkPos.asLong("
},
{
"pattern": "\\.registryAccess\\(\\)\\.lookupOrThrow\\(",
"replacement": ".registryAccess().registryOrThrow("
},
{
"pattern": "\\.getPos\\(\\)\\.x\\(\\)",
"replacement": ".getPos().x"
},
{
"pattern": "\\.getPos\\(\\)\\.z\\(\\)",
"replacement": ".getPos().z"
},
{
"pattern": "\\.getChunkPos\\(\\)\\.x\\(\\)",
"replacement": ".getChunkPos().x"
},
{
"pattern": "\\.getChunkPos\\(\\)\\.z\\(\\)",
"replacement": ".getChunkPos().z"
},
{
"pattern": "IdentifierArgument",
"replacement": "ResourceLocationArgument"
},
{
"pattern": "IdentifierArgument\\.identifier\\(\\)",
"replacement": "ResourceLocationArgument.id()"
},
{
"pattern": "BuiltInRegistries\\.([A-Z_]+)\\.getValue\\(",
"replacement": "BuiltInRegistries.$1.get("
},
{
"pattern": "\\blevel\\.getMinY\\(",
"replacement": "level.getMinBuildHeight("
},
{
"pattern": "(?ms)^ if \\(!Items\\.[A-Z_]+\\.builtInRegistryHolder\\(\\)\\.areComponentsBound\\(\\)\\) \\{\\n.*?^ \\}\\n",
"replacement": ""
}
],
"files": {
"art/arcane/iris/modded/command/NativeTaggedItemsTest.java": [
{
"pattern": "\\.getBooleanOr\\(\"selection_tool\", false\\)",
"replacement": ".getBoolean(\"selection_tool\")"
},
{
"pattern": "assertTrue\\(item\\.stack\\(\\)\\.get\\(DataComponents\\.TOOLTIP_DISPLAY\\)\\.hiddenComponents\\(\\)\\.contains\\(DataComponents\\.UNBREAKABLE\\)\\);",
"replacement": "assertEquals(false, item.stack().get(DataComponents.UNBREAKABLE).showInTooltip());"
}
],
"art/arcane/iris/modded/service/ModdedPackUpdateServiceTest.java": [
{
"pattern": "import net\\.minecraft\\.server\\.permissions\\.LevelBasedPermissionSet;\\n",
"replacement": ""
},
{
"pattern": "when\\(handle\\.permissions\\(\\)\\)\\.thenReturn\\(LevelBasedPermissionSet\\.ALL\\);",
"replacement": "when(handle.hasPermission(2)).thenReturn(false);"
},
{
"pattern": "handle\\.hasPermission\\(",
"replacement": "handle.hasPermissions("
},
{
"pattern": "handle\\.nameAndId\\(",
"replacement": "handle.getGameProfile("
},
{
"pattern": "handle\\.level\\(\\)",
"replacement": "handle.serverLevel()"
}
],
"art/arcane/iris/modded/service/ModdedChunkUpdateServiceTest.java": [
{
"pattern": "when\\(level\\.getMaxY\\(\\)\\)\\.thenReturn\\(319\\);",
"replacement": "when(level.getMaxBuildHeight()).thenReturn(320);"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnPopulationTest.java": [
{
"pattern": "when\\(level\\.getMaxY\\(\\)\\)\\.thenReturn\\(320\\);",
"replacement": "when(level.getMaxBuildHeight()).thenReturn(321);"
},
{
"pattern": "rules\\.get\\(GameRules\\.SPAWN_MOBS\\)",
"replacement": "rules.getBoolean(GameRules.RULE_DOMOBSPAWNING)"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeTeleportSafetyTest.java": [
{
"pattern": "when\\(level\\.getMaxY\\(\\)\\)\\.thenReturn\\(16\\);",
"replacement": "when(level.getMaxBuildHeight()).thenReturn(17);"
},
{
"pattern": "import net.minecraft.server.level.ServerLevel;",
"replacement": "import net.minecraft.server.level.ServerLevel;\nimport net.minecraft.server.level.ChunkResult;\nimport net.minecraft.world.level.chunk.ChunkAccess;"
},
{
"pattern": "CompletableFuture<Void> loaded",
"replacement": "CompletableFuture<ChunkResult<ChunkAccess>> loaded"
},
{
"pattern": "\\.addTicketAndLoadWithRadius\\(any\\(\\), any\\(\\), anyInt\\(\\)\\)",
"replacement": ".getChunkFutureMainThread(anyInt(), anyInt(), any(), anyBoolean())"
},
{
"pattern": "loaded\\.complete\\(null\\)",
"replacement": "loaded.complete(ChunkResult.of(mock(ChunkAccess.class)))"
},
{
"pattern": "\\.removeTicketWithRadius\\(any\\(\\), any\\(\\), anyInt\\(\\)\\)",
"replacement": ".removeRegionTicket(any(), any(), anyInt(), any())"
},
{
"pattern": "import static org.mockito.ArgumentMatchers.anyInt;",
"replacement": "import static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyBoolean;"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java": [
{
"pattern": "import net\\.minecraft\\.world\\.level\\.levelgen\\.feature\\.FallenTreeFeature;\\n",
"replacement": ""
},
{
"pattern": " assertEquals\\(\"fallen_trees\", NativeStructureOperations\\.classifyFeature\\(mock\\(FallenTreeFeature\\.class\\)\\)\\);\\n",
"replacement": ""
}
],
"art/arcane/iris/nativegen/NativeStructureReferenceRepairTest.java": [
{
"pattern": "\\borigin\\.x\\(\\)",
"replacement": "origin.x"
},
{
"pattern": "\\borigin\\.z\\(\\)",
"replacement": "origin.z"
},
{
"pattern": "\\bcandidate\\.x\\(\\)",
"replacement": "candidate.x"
},
{
"pattern": "\\bcandidate\\.z\\(\\)",
"replacement": "candidate.z"
},
{
"pattern": "\\borigin\\.pack\\(",
"replacement": "origin.toLong("
},
{
"pattern": "\"lookupOrThrow\"",
"replacement": "\"registryOrThrow\""
},
{
"pattern": "\"lookup\"",
"replacement": "\"registry\""
}
],
"art/arcane/iris/modded/ModdedNativeStructureWorldgenAccessTest.java": [
{
"pattern": "\\bgenerationCenter\\.x\\(\\)",
"replacement": "generationCenter.x"
},
{
"pattern": "\\bgenerationCenter\\.z\\(\\)",
"replacement": "generationCenter.z"
},
{
"pattern": "\\borigin\\.pack\\(",
"replacement": "origin.toLong("
},
{
"pattern": "\\.updateNeighborsAt\\(",
"replacement": ".blockUpdated("
},
{
"pattern": "(Direction\\.(?:NORTH|EAST)), position, position\\.(north|east)\\(\\),\\s*Blocks\\.DIRT\\.defaultBlockState\\(\\),",
"replacement": "$1, Blocks.DIRT.defaultBlockState(), position, position.$2(),"
},
{
"pattern": "\"getMinY\"",
"replacement": "\"getMinBuildHeight\""
}
],
"art/arcane/iris/modded/ModdedTileParityTest.java": [
{
"pattern": "import net\\.minecraft\\.world\\.level\\.block\\.entity\\.SignTextSlot;\\n",
"replacement": ""
},
{
"pattern": "import net\\.minecraft\\.world\\.level\\.storage\\.TagValueInput;\\n",
"replacement": ""
},
{
"pattern": "\\.loadWithComponents\\(TagValueInput\\.create\\(\\s*ProblemReporter\\.DISCARDING, registries, (tile\\.nativeData\\(\\)\\.payload\\(\\)|\\(\\(ModdedTileData\\) tile\\)\\.nativeData\\(\\)\\.payload\\(\\))\\)\\)",
"replacement": ".loadWithComponents($1, registries)"
},
{
"pattern": "\\.getText\\(SignTextSlot\\.FRONT\\)",
"replacement": ".getFrontText()"
},
{
"pattern": "\\.getText\\(SignTextSlot\\.BACK\\)",
"replacement": ".getBackText()"
},
{
"pattern": "\\.getMessages\\(false\\)\\.get\\((\\d+)\\)",
"replacement": ".getMessages(false)[$1]"
},
{
"pattern": "out\\.writeShort\\(28\\);",
"replacement": "out.writeShort(37);"
}
],
"art/arcane/iris/modded/ModdedEntitySpawnerParityTest.java": [
{
"pattern": "MobSpawnType\\.SPAWN_ITEM_USE",
"replacement": "MobSpawnType.SPAWN_EGG"
},
{
"pattern": "MobSpawnType\\.LOAD",
"replacement": "MobSpawnType.EVENT"
}
],
"art/arcane/iris/modded/service/ModdedWorldBoundaryTest.java": [
{
"pattern": "\\.setSafeZone\\(",
"replacement": ".setDamageSafeZone("
},
{
"pattern": "\\.getSafeZone\\(",
"replacement": ".getDamageSafeZone("
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnBiomeTablesTest.java": [
{
"pattern": "import net.minecraft.resources.ResourceLocation;",
"replacement": "import net.minecraft.resources.ResourceLocation;\nimport net.minecraft.resources.ResourceKey;\nimport net.minecraft.core.registries.Registries;"
},
{
"pattern": "registry\\.get\\((ResourceLocation\\.parse\\(\"[^\"]+\"\\))\\)",
"replacement": "registry.getHolder(ResourceKey.create(Registries.BIOME, $1))"
}
],
"art/arcane/iris/modded/NativeItemTranslatorTest.java": [
{
"pattern": "assertEquals\\(73F, stack\\.get\\(DataComponents\\.CUSTOM_MODEL_DATA\\)\\.floats\\(\\)\\.getFirst\\(\\), 0F\\);",
"replacement": "assertEquals(73, stack.get(DataComponents.CUSTOM_MODEL_DATA).value());"
},
{
"pattern": "\\.getString\\(\"marker\"\\)\\.orElseThrow\\(\\)",
"replacement": ".getString(\"marker\")"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeHarvestSessionTest.java": [
{
"pattern": "\\.getSelectedItem\\(",
"replacement": ".getSelected("
},
{
"pattern": " doAnswer\\(invocation -> \\{ item\\.set\\(invocation\\.getArgument\\(0\\)\\); return null; \\}\\)\\n \\.when\\(inventory\\)\\.setSelectedItem\\(any\\(ItemStack\\.class\\)\\);\\n",
"replacement": ""
}
],
"art/arcane/iris/modded/command/IrisModdedCommandsTeleportTest.java": [
{
"pattern": "\\.addTicketAndLoadWithRadius\\(any\\(\\), any\\(\\), anyInt\\(\\)\\)",
"replacement": ".getChunkFutureMainThread(anyInt(), anyInt(), any(), anyBoolean())"
},
{
"pattern": "import static org.mockito.ArgumentMatchers.anyInt;",
"replacement": "import static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.ArgumentMatchers.anyBoolean;"
}
],
"art/arcane/iris/modded/NativeRegistryAccessTest.java": [
{
"pattern": "\\.registryAccess\\(\\)\\.registryOrThrow\\(",
"replacement": ".registryAccess().lookupOrThrow("
},
{
"pattern": "\\.reloadableRegistries\\(\\)\\.lookup\\(",
"replacement": ".reloadableRegistries().get("
},
{
"pattern": "import net.minecraft.core.HolderLookup;",
"replacement": "import net.minecraft.core.HolderLookup;\nimport net.minecraft.core.RegistryAccess;"
},
{
"pattern": "HolderLookup\\.Provider reloadedRegistries = mock\\(HolderLookup\\.Provider.class",
"replacement": "RegistryAccess.Frozen reloadedRegistries = mock(RegistryAccess.Frozen.class"
}
],
"art/arcane/iris/modded/ModdedEntityPersistenceTest.java": [
{
"pattern": "\\.entityTags\\(",
"replacement": ".getTags("
}
],
"art/arcane/iris/modded/ModdedLootApplierTest.java": [
{
"pattern": "import net.minecraft.core.BlockPos;",
"replacement": "import net.minecraft.core.BlockPos;\nimport net.minecraft.core.Direction;"
},
{
"pattern": "ChestBlock\\.getConnectedBlockPos\\(leftPos, leftState\\)\\)\\.thenReturn\\(rightPos\\)",
"replacement": "ChestBlock.getConnectedDirection(leftState)).thenReturn(Direction.EAST)"
}
],
"art/arcane/iris/modded/ModdedBlockResolutionContractTest.java": [
{
"pattern": ", Blocks\\.PALE_MOSS_BLOCK",
"replacement": ""
},
{
"pattern": "Blocks\\.SULFUR_SPIKE, ",
"replacement": ""
},
{
"pattern": "\"minecraft:sulfur_spike\", ",
"replacement": ""
},
{
"pattern": "copperChestVariantsReceiveIrisLootAndPostLoadUpdates",
"replacement": "chestsReceiveIrisLootAndPostLoadUpdates"
},
{
"pattern": "\"copper_chest\", \"exposed_copper_chest\", \"weathered_copper_chest\",\\s*\"oxidized_copper_chest\", \"waxed_copper_chest\", \"waxed_exposed_copper_chest\",\\s*\"waxed_weathered_copper_chest\", \"waxed_oxidized_copper_chest\"",
"replacement": "\"chest\", \"trapped_chest\""
},
{
"pattern": "sulfurAndDripstoneTipsReceivePostLoadUpdates",
"replacement": "dripstoneTipsReceivePostLoadUpdates"
}
],
"art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java": [
{
"pattern": "(?ms)^ BuiltInRegistries\\.BLOCK\\.prepareTagReload\\(new TagLoader\\.LoadResult<>.*?\\)\\)\\)\\)\\.apply\\(\\);\\n",
"replacement": ""
}
],
"art/arcane/iris/modded/command/ModdedLanguageCommandsTest.java": [
{
"pattern": "new ClickEvent\\.RunCommand\\(",
"replacement": "new ClickEvent(ClickEvent.Action.RUN_COMMAND, "
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeCommandTextTest.java": [
{
"pattern": "\\(\\(ClickEvent\\.RunCommand\\) component\\.getStyle\\(\\)\\.getClickEvent\\(\\)\\)\\.command\\(\\)",
"replacement": "component.getStyle().getClickEvent().getValue()"
},
{
"pattern": "\\(\\(HoverEvent\\.ShowText\\) component\\.getStyle\\(\\)\\.getHoverEvent\\(\\)\\)\\.value\\(\\)",
"replacement": "component.getStyle().getHoverEvent().getValue(HoverEvent.Action.SHOW_TEXT)"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeChunkRegenerationTest.java": [
{
"pattern": "\\.markUnsaved\\(\\)",
"replacement": ".setUnsaved(true)"
}
],
"art/arcane/iris/modded/ModdedSavedBiomeConsumersTest.java": [
{
"pattern": "player\\.level\\(\\)",
"replacement": "player.serverLevel()"
}
]
}
}