Added 26.1.2 and 26.2 mod targets

- Mod targets 26.2 and 26.1.2 (inheriting 26.2) with Fabric 0.19.5, Forge 65.1.3 and 64.1.4, NeoForge 26.2.0.89 and 26.1.2.116
- The 26.3 target reads the minecraft26_3 native access rules
- Per-version pack.mcmeta ranges for 26.2 and 26.1.2
- 26.2 and 26.1.2 test overlays and replacements for the pre-26.3 biome, spawn, carver and sign APIs
- NeoForge compiles against the loader's own ASM while the mod jar keeps the bundled ASM
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-16 11:07:09 -04:00
1 parent 0073002b57
commit 4daae925c6
9 files changed
+624 -2

No files matched your search

@@ -0,0 +1,101 @@
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.Identifier;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.entity.EntityTypes;
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\":{\"water_color\":\"#3f76e4\"},\"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, Identifier.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, Identifier.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().modifiableBiomeInfo().get().climateSettings().temperature(), 0.0F);
assertEquals(0x102030, biome.value().modifiableBiomeInfo().get().effects().waterColor() & 0xFFFFFF);
assertTrue(biome.value().getGenerationSettings().hasFeature(ore.value()));
assertTrue(biome.value().getMobSettings().getMobs(MobCategory.MONSTER).unwrap().stream()
.anyMatch(spawn -> spawn.value().type() == EntityTypes.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, 5,
new MobSpawnSettings.SpawnerData(EntityTypes.ZOMBIE, 1, 2));
builder.getGenerationSettings().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, ore);
}
@Override
public MapCodec<? extends BiomeModifier> codec() {
throw new UnsupportedOperationException();
}
}
}
@@ -0,0 +1,7 @@
{
"pack": {
"description": "Iris client resources and mod data",
"min_format": [84, 0],
"max_format": [101, 1]
}
}
@@ -0,0 +1,32 @@
{
"global": [
{
"pattern": "\\bEntityTypes\\b",
"replacement": "EntityType"
},
{
"pattern": "\\bSpeleothemBlock\\b",
"replacement": "PointedDripstoneBlock"
},
{
"pattern": "\\bSpeleothemThickness\\b",
"replacement": "DripstoneThickness"
}
],
"files": {
"art/arcane/iris/modded/ModdedBlockResolutionContractTest.java": [
{
"pattern": "Blocks\\.SULFUR_SPIKE, ",
"replacement": ""
},
{
"pattern": "\"minecraft:sulfur_spike\", ",
"replacement": ""
},
{
"pattern": "sulfurAndDripstoneTipsReceivePostLoadUpdates",
"replacement": "dripstoneTipsReceivePostLoadUpdates"
}
]
}
}
@@ -0,0 +1,7 @@
{
"pack": {
"description": "Iris client resources and mod data",
"min_format": [88, 0],
"max_format": [107, 1]
}
}
@@ -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.Identifier;
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, Identifier.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,78 @@
{
"global": [
{
"pattern": "new MobSpawnSettings\\.SpawnerData\\((EntityTypes\\.[A-Z_]+), UniformInt\\.of\\((\\d+), (\\d+)\\)\\)",
"replacement": "new MobSpawnSettings.SpawnerData($1, $2, $3)"
}
],
"files": {
"art/arcane/volmlib/nativelib/minecraft26_2/fabric/NativeFabricBiomeDefinitionTest.java": [
{
"pattern": "import net\\.minecraft\\.world\\.level\\.levelgen\\.carver\\.WorldCarver;",
"replacement": "import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;"
},
{
"pattern": "import net\\.minecraft\\.world\\.level\\.levelgen\\.feature\\.Feature;",
"replacement": "import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;"
},
{
"pattern": "mock\\(Feature\\.class\\)",
"replacement": "mock(ConfiguredFeature.class)"
},
{
"pattern": "MappedRegistry<WorldCarver> carvers = new MappedRegistry<>\\(Registries\\.CARVER,",
"replacement": "MappedRegistry<ConfiguredWorldCarver<?>> carvers = new MappedRegistry<>(Registries.CONFIGURED_CARVER,"
},
{
"pattern": "\\\\\"features\\\\\":\\[\\]\\}\";",
"replacement": "\\\\\"features\\\\\":[],\\\\\"spawners\\\\\":{},\\\\\"spawn_costs\\\\\":{}}\";"
}
],
"art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldgenScanTest.java": [
{
"pattern": "import net\\.minecraft\\.world\\.level\\.levelgen\\.carver\\.WorldCarver;",
"replacement": "import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;"
},
{
"pattern": "import net\\.minecraft\\.world\\.level\\.levelgen\\.feature\\.Feature;",
"replacement": "import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;"
},
{
"pattern": "mock\\(Feature\\.class\\)",
"replacement": "mock(ConfiguredFeature.class)"
},
{
"pattern": "MappedRegistry<WorldCarver> carvers = new MappedRegistry<>\\(Registries\\.CARVER,",
"replacement": "MappedRegistry<ConfiguredWorldCarver<?>> carvers = new MappedRegistry<>(Registries.CONFIGURED_CARVER,"
},
{
"pattern": "\\]\\]\\}\";",
"replacement": "]],\\\\\"spawners\\\\\":{},\\\\\"spawn_costs\\\\\":{}}\";"
}
],
"art/arcane/iris/modded/ModdedTileParityTest.java": [
{
"pattern": "import net\\.minecraft\\.world\\.level\\.block\\.entity\\.SignTextSlot;\\n",
"replacement": ""
},
{
"pattern": "\\.getText\\(SignTextSlot\\.FRONT\\)",
"replacement": ".getFrontText()"
},
{
"pattern": "\\.getText\\(SignTextSlot\\.BACK\\)",
"replacement": ".getBackText()"
},
{
"pattern": "\\.getMessages\\(false\\)\\.get\\((\\d+)\\)",
"replacement": ".getMessages(false)[$1]"
}
],
"art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java": [
{
"pattern": "(?ms)^ BuiltInRegistries\\.BLOCK\\.prepareTagReload\\(new TagLoader\\.LoadResult<>.*?\\)\\)\\)\\)\\.apply\\(\\);\\n",
"replacement": ""
}
]
}
}
+6 -1
View File
@@ -168,13 +168,18 @@ dependencies {
"art.arcane:core:${irisVersion}".toString(),
"art.arcane:spi:${irisVersion}".toString(),
libs.paralithic,
libs.asm,
libs.lru,
libs.caffeine,
'com.fulcrumgenomics:jlibdeflate:0.2.0',
libs.dom4j,
libs.jaxen
]
add('bundle', libs.asm)
add('devBundle', 'org.ow2.asm:asm') {
version {
prefer(libs.versions.asm.get())
}
}
shared.each { Object notation ->
['bundle', 'devBundle'].each { String configuration ->
add(configuration, notation) {
@@ -0,0 +1,89 @@
package art.arcane.volmlib.nativelib.minecraft26_2.neoforge;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerAccess;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels;
import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions;
import net.minecraft.SharedConstants;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.function.BooleanSupplier;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NativeNeoForgeServerAccessTest {
private static final ResourceKey<Level> RUNTIME_DIMENSION =
ResourceKey.create(Registries.DIMENSION, Identifier.fromNamespaceAndPath("irisworldgen", "runtime"));
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@After
public void forgetLevelSnapshot() {
ModdedServerLevels.forget();
}
@Test
public void runtimeLevelsJoinAndLeaveTheServerTickView() throws ReflectiveOperationException {
ModdedServerAccess access = new NativeNeoForgeLoader(
new NativeLoaderOptions("irisworldgen", "treefeller", BooleanSupplier::getAsBoolean)).serverAccess();
MinecraftServer server = serverWithLiveLevelMap();
ServerLevel level = mock(ServerLevel.class);
when(level.dimension()).thenReturn(RUNTIME_DIMENSION);
assertNull(access.putLevelIfAbsent(server, RUNTIME_DIMENSION, level));
assertTrue(access.hasLevel(server, RUNTIME_DIMENSION));
assertTrue(ticks(server, level));
assertSame(level, ModdedServerLevels.level(server, RUNTIME_DIMENSION));
assertSame(level, access.removeLevel(server, RUNTIME_DIMENSION));
assertFalse(access.hasLevel(server, RUNTIME_DIMENSION));
assertFalse(ticks(server, level));
assertNull(ModdedServerLevels.level(server, RUNTIME_DIMENSION));
assertNull(access.putLevel(server, RUNTIME_DIMENSION, level));
assertTrue(ticks(server, level));
assertSame(level, ModdedServerLevels.level(server, RUNTIME_DIMENSION));
}
private static MinecraftServer serverWithLiveLevelMap() throws ReflectiveOperationException {
MinecraftServer server = mock(MinecraftServer.class);
Field levels = MinecraftServer.class.getDeclaredField("levels");
levels.setAccessible(true);
levels.set(server, new LinkedHashMap<ResourceKey<Level>, ServerLevel>());
doCallRealMethod().when(server).getAllLevels();
doCallRealMethod().when(server).markWorldsDirty();
return server;
}
private static boolean ticks(MinecraftServer server, ServerLevel expected) throws ReflectiveOperationException {
Method worldArray = MinecraftServer.class.getDeclaredMethod("getWorldArray");
worldArray.setAccessible(true);
for (ServerLevel level : (ServerLevel[]) worldArray.invoke(server)) {
if (level == expected) {
return true;
}
}
return false;
}
}
+51 -1
View File
@@ -22,6 +22,56 @@
"fabricPermissions": "0.3.1",
"slf4j": "2.0.9"
},
"26.1.2": {
"java": 25,
"fabricRemap": false,
"fabricLoader": "0.19.5",
"forge": "26.1.2-64.1.4",
"forgeRange": "[64,65)",
"neoForge": "26.1.2.116",
"neoForgeRange": "[26.1.2,26.2)",
"nativeAccess": "minecraft26_1_2",
"overlays": [
"26.2",
"26.1.2"
],
"fabricModules": {
"fabric-api-base": "2.0.3+ece063234c",
"fabric-registry-sync-v0": "7.1.1+2fa62b4e4c",
"fabric-resource-loader-v1": "2.0.10+7c44c7324c",
"fabric-lifecycle-events-v1": "4.1.1+df84eb3d4c",
"fabric-command-api-v2": "3.0.5+e2bdee784c",
"fabric-events-interaction-v0": "5.2.3+07b380be4c",
"fabric-networking-api-v1": "6.3.2+a217cccf4c",
"fabric-rendering-v1": "23.3.1+e9207d814c",
"fabric-key-mapping-api-v1": "2.0.4+e2bdee784c",
"fabric-permission-api-v1": "1.0.3+5730ecca4c"
},
"slf4j": "2.0.17"
},
"26.2": {
"java": 25,
"fabricRemap": false,
"fabricLoader": "0.19.5",
"forge": "26.2-65.1.3",
"forgeRange": "[65,66)",
"neoForge": "26.2.0.89",
"neoForgeRange": "[26.2,26.3)",
"nativeAccess": "minecraft26_2",
"fabricModules": {
"fabric-api-base": "2.0.4+ece063239e",
"fabric-registry-sync-v0": "7.1.1+c7bd5b8e9e",
"fabric-resource-loader-v1": "2.0.13+9edec1269e",
"fabric-lifecycle-events-v1": "4.1.4+29b6eb019e",
"fabric-command-api-v2": "3.1.0+00cb03469e",
"fabric-events-interaction-v0": "5.2.8+515ac5339e",
"fabric-networking-api-v1": "6.3.4+2989c6a09e",
"fabric-rendering-v1": "25.3.3+515ac5339e",
"fabric-key-mapping-api-v1": "2.0.5+e2bdee789e",
"fabric-permission-api-v1": "1.0.5+e442d8d09e"
},
"slf4j": "2.0.17"
},
"26.3": {
"java": 25,
"fabricRemap": false,
@@ -30,7 +80,7 @@
"forgeRange": "[66,67)",
"neoForge": "26.3.0.83",
"neoForgeRange": "[26.3.0.65-beta,26.4)",
"nativeAccess": "minecraft26_2",
"nativeAccess": "minecraft26_3",
"slf4j": "2.0.17"
}
}