Added 1.21.1 mod targets

- Build Fabric, Forge and NeoForge per target listed in gradle/mod-targets.json (1.21.1, 26.3)
- Add 1.21.1 adapter source overlays under adapters/*/src/versions/1.21.1
- Add verifyModdedMatrix and per-target Java runtime verification of loader jars
- Re-inject persistent dimensions from the levels-ready hook before the watchdog starts
- Wait for queued chunks and the final save before pregen completes
- Add the 1.21.1 data version and datapack supported_formats range
- Cascade regions whose sea or shore pool loses every biome
- Use Gson calls available on the 1.21.1 runtime
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-16 10:25:19 -04:00
1 parent 0ca33068f3
commit 62062c4a95
58 files changed
+3279 -133

No files matched your search

@@ -0,0 +1,29 @@
package art.arcane.iris.forge;
import art.arcane.iris.modded.IrisModdedChunkGenerator;
import art.arcane.iris.modded.ModdedEngineBootstrap;
import art.arcane.iris.modded.ModdedLifecycleCallbacks;
import art.arcane.iris.modded.service.ModdedTreeFellerService;
import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkGeneratorDefinition;
import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeLoader;
import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeBootstrap;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader;
@Mod("irisworldgen")
public final class IrisForgeBootstrap {
public IrisForgeBootstrap(FMLJavaModLoadingContext context) {
NativeForgeLoader loader = new NativeForgeLoader(new NativeLoaderOptions(
"irisworldgen", "treefeller", ModdedTreeFellerService::runBreakProbe));
NativeChunkGeneratorDefinition generator = IrisModdedChunkGenerator.DEFINITION;
ModdedEngineBootstrap.bootCommon(loader, "Forge " + FMLLoader.versionInfo().forgeVersion(),
() -> NativeForgeBootstrap.registerGenerators(context, generator, "block_drops"));
ForgeProtocolNetworking.register();
if (loader.clientEnvironment()) {
IrisForgeClient.init(context);
}
NativeForgeBootstrap.install(context, loader, ModdedLifecycleCallbacks.create());
}
}
@@ -0,0 +1,25 @@
package art.arcane.iris.forge;
import art.arcane.iris.client.IrisClient;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeClientHooks;
import art.arcane.iris.client.IrisClientHud;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolChannel;
public final class IrisForgeClient {
private IrisForgeClient() {
}
public static void init(FMLJavaModLoadingContext context) {
IrisClient.bindSender(IrisForgeClient::sendToServer);
NativeForgeClientHooks.install(context, IrisClientHud.binding(), IrisClient::onWorldJoin, IrisClient::onDisconnect);
}
private static void sendToServer(byte[] frame) {
NativeProtocolChannel channel = ForgeProtocolNetworking.channel();
if (channel == null) {
return;
}
channel.sendToServer(frame);
}
}
@@ -0,0 +1,28 @@
modLoader = "javafml"
loaderVersion = "[52,53)"
license = "GPL-3.0"
issueTrackerURL = "https://github.com/VolmitSoftware/Iris/issues"
[[mods]]
modId = "irisworldgen"
version = "${version}"
displayName = "Iris"
description = "Iris World Generation Engine (Forge adapter - native chunk generator, explicit Iris world datapack workflow, engine lifecycle)"
authors = "Arcane Arts (Volmit Software)"
credits = "cyberpwn, NextdoorPsycho, Vatuu"
displayURL = "https://docs.volmit.com/iris/"
logoFile = "assets/irisworldgen/icon.png"
[[dependencies.irisworldgen]]
modId = "forge"
mandatory = true
versionRange = "[52,53)"
ordering = "NONE"
side = "BOTH"
[[dependencies.irisworldgen]]
modId = "minecraft"
mandatory = true
versionRange = "[${minecraftVersion}]"
ordering = "NONE"
side = "BOTH"
@@ -0,0 +1,100 @@
package art.arcane.volmlib.nativelib.minecraft26_2.forge;
import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeRegistryDefinitions;
import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.Lifecycle;
import com.mojang.serialization.MapCodec;
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.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.MobSpawnSettings;
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.minecraftforge.common.world.BiomeModifier;
import net.minecraftforge.common.world.ModifiableBiomeInfo;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import java.util.function.BooleanSupplier;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class NativeForgeBiomeDefinitionTest {
private static final String BIOME_REGISTRY = "minecraft:worldgen/biome";
private static final String BIOME_KEY = "iris:biomes/forge_modified";
private static final String SOURCE_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\":[],\"spawners\":{},\"spawn_costs\":{}}";
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void generatedBiomeDefinitionExcludesForgeBiomeModifierOutput() {
MappedRegistry<PlacedFeature> features = new MappedRegistry<>(Registries.PLACED_FEATURE, Lifecycle.stable());
Holder.Reference<PlacedFeature> ore = features.register(
ResourceKey.create(Registries.PLACED_FEATURE, ResourceLocation.parse("iris:modded_ore")),
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());
RegistryAccess access = new RegistryAccess.ImmutableRegistryAccess(List.of(biomes, features, carvers));
Biome source = Biome.DIRECT_CODEC.parse(RegistryOps.create(JsonOps.INSTANCE, access),
JsonParser.parseString(SOURCE_JSON)).getOrThrow();
Holder.Reference<Biome> biome = biomes.register(
ResourceKey.create(Registries.BIOME, ResourceLocation.parse(BIOME_KEY)), source, RegistrationInfo.BUILT_IN);
biomes.freeze();
biome.value().modifiableBiomeInfo().applyBiomeModifiers(biome, List.of(new ModdedContent(ore)));
NativeRegistryDefinitions definitions = new NativeRegistryDefinitions(() -> access,
new NativeForgeLoader(new NativeLoaderOptions("irisworldgen", "treefeller",
BooleanSupplier::getAsBoolean))::unmodifiedBiome);
assertEquals(0.1F, biome.value().getBaseTemperature(), 0.0F);
assertEquals(0x102030, biome.value().getWaterColor() & 0xFFFFFF);
assertTrue(biome.value().getGenerationSettings().hasFeature(ore.value()));
assertTrue(biome.value().getMobSettings().getMobs(MobCategory.MONSTER).unwrap().stream()
.anyMatch(spawn -> spawn.type == EntityType.ZOMBIE));
assertEquals(JsonParser.parseString(definitions.canonicalDefinition(BIOME_REGISTRY, BIOME_KEY, SOURCE_JSON).value()),
JsonParser.parseString(definitions.generatedDefinition(BIOME_REGISTRY, BIOME_KEY).value()));
}
private record ModdedContent(Holder<PlacedFeature> ore) implements BiomeModifier {
@Override
public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
if (phase != Phase.ADD) {
return;
}
builder.getClimateSettings().setTemperature(0.1F);
builder.getSpecialEffects().waterColor(0x102030);
builder.getMobSpawnSettings().addSpawn(MobCategory.MONSTER, new MobSpawnSettings.SpawnerData(EntityType.ZOMBIE, 5, 1, 2));
builder.getGenerationSettings().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, ore);
}
@Override
public MapCodec<? extends BiomeModifier> codec() {
throw new UnsupportedOperationException();
}
}
}
@@ -0,0 +1,54 @@
package art.arcane.volmlib.nativelib.minecraft26_2.forge;
import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.living.MobSpawnEvent;
import net.minecraftforge.eventbus.api.Event;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NativeForgeSpawnPositionTest {
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void chunkGenerationSpawnsFireThePositionCheckEventAndHonorItsDenial() {
NativeForgeLoader loader = new NativeForgeLoader(
new NativeLoaderOptions("irisworldgen", "treefeller", BooleanSupplier::getAsBoolean));
ServerLevelAccessor level = mock(ServerLevelAccessor.class);
Mob mob = mock(Mob.class);
when(mob.checkSpawnRules(level, MobSpawnType.CHUNK_GENERATION)).thenReturn(true);
when(mob.checkSpawnObstruction(level)).thenReturn(true);
AtomicReference<MobSpawnType> observed = new AtomicReference<>();
Consumer<MobSpawnEvent.PositionCheck> listener = event -> {
observed.set(event.getSpawnType());
event.setResult(Event.Result.DENY);
};
MinecraftForge.EVENT_BUS.addListener(listener);
try {
assertFalse(loader.checkSpawnPosition(mob, level, MobSpawnType.CHUNK_GENERATION));
} finally {
MinecraftForge.EVENT_BUS.unregister(listener);
}
assertEquals(MobSpawnType.CHUNK_GENERATION, observed.get());
assertTrue(loader.checkSpawnPosition(mob, level, MobSpawnType.CHUNK_GENERATION));
}
}