Merge remote-tracking branch 'origin/1.21.1' into 26.1

This commit is contained in:
embeddedt 2026-05-16 14:11:53 -04:00
commit 433a0e5b70
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
9 changed files with 197 additions and 171 deletions

View File

@ -0,0 +1,7 @@
package org.embeddedt.modernfix.chunk;
import net.minecraft.world.level.chunk.Palette;
public interface ExtendedPalettedContainer<T> {
Palette<T> mfix$getPalette();
}

View File

@ -0,0 +1,18 @@
package org.embeddedt.modernfix.common.mixin.core;
import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer;
import org.embeddedt.modernfix.chunk.ExtendedPalettedContainer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(PalettedContainer.class)
public class PalettedContainerMixin<T> implements ExtendedPalettedContainer<T> {
@Shadow
private volatile PalettedContainer.Data<T> data;
@Override
public Palette<T> mfix$getPalette() {
return this.data.palette();
}
}

View File

@ -0,0 +1,62 @@
package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.SurfaceRules;
import org.embeddedt.modernfix.world.gen.ExtendedSurfaceContext;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.Set;
@Mixin(SurfaceRules.BiomeConditionSource.class)
public class BiomeConditionSourceMixin {
@Shadow
@Final
public List<ResourceKey<Biome>> biomes;
/**
* @author Mojang, embeddedt
* @reason Hoist evaluation of the biome conditions where possible, in cases where we know the chunk contains
* no matching biomes, or only matching biomes
*/
@Inject(method = "apply(Lnet/minecraft/world/level/levelgen/SurfaceRules$Context;)Lnet/minecraft/world/level/levelgen/SurfaceRules$Condition;", at = @At("HEAD"), cancellable = true)
private void mfix$optimizeCondition(SurfaceRules.Context p_context, CallbackInfoReturnable<SurfaceRules.Condition> cir) {
var possibleBiomes = ((ExtendedSurfaceContext)(Object)p_context).mfix$getPossibleBiomes();
if (possibleBiomes != null) {
if (mfix$guaranteedNoMatch(possibleBiomes)) {
cir.setReturnValue(() -> false);
} else if (mfix$alwaysMatches(possibleBiomes)) {
cir.setReturnValue(() -> true);
}
}
}
private boolean mfix$guaranteedNoMatch(Set<ResourceKey<Biome>> possibleBiomesInChunk) {
var testBiomes = this.biomes;
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < testBiomes.size(); i++) {
if (possibleBiomesInChunk.contains(testBiomes.get(i))) {
return false;
}
}
return true;
}
private boolean mfix$alwaysMatches(Set<ResourceKey<Biome>> possibleBiomesInChunk) {
var testBiomes = this.biomes;
// Check each of the biomes in the chunk and see if we'd always match it
for (var biome : possibleBiomesInChunk) {
if (!testBiomes.contains(biome)) {
// at least one biome would not match
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,61 @@
package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.RandomState;
import org.embeddedt.modernfix.chunk.ExtendedPalettedContainer;
import org.embeddedt.modernfix.world.gen.ExtendedSurfaceContext;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.NoSuchElementException;
import java.util.Set;
@Mixin(NoiseBasedChunkGenerator.class)
public class NoiseBasedChunkGeneratorMixin {
@SuppressWarnings("unchecked")
private static void mfix$accumulate(Set<ResourceKey<Biome>> chunkBiomes, LevelChunkSection section) {
var palette = ((ExtendedPalettedContainer<Holder<Biome>>)section.getBiomes()).mfix$getPalette();
for (int i = 0; i < palette.getSize(); i++) {
chunkBiomes.add(palette.valueFor(i).unwrapKey().orElseThrow());
}
}
private static Set<ResourceKey<Biome>> mfix$obtainBiomes(WorldGenRegion region, int chunkRadius) {
Set<ResourceKey<Biome>> chunkBiomes = new ReferenceOpenHashSet<>();
ChunkPos center = region.getCenter();
for (int z = center.z() - chunkRadius; z <= center.z() + chunkRadius; z++) {
for (int x = center.x() - chunkRadius; x <= center.x() + chunkRadius; x++) {
var chunk = region.getChunk(x, z);
for (var section : chunk.getSections()) {
mfix$accumulate(chunkBiomes, section);
}
}
}
return chunkBiomes;
}
/**
* @author embeddedt
* @reason scan for all biomes in the chunk and its neighbors before building surface
*/
@Inject(method = "buildSurface(Lnet/minecraft/server/level/WorldGenRegion;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/chunk/ChunkAccess;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/NoiseBasedChunkGenerator;buildSurface(Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/world/level/levelgen/WorldGenerationContext;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/biome/BiomeManager;Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;)V"))
private void mfix$findNearbyBiomes(WorldGenRegion level, StructureManager structureManager, RandomState random, ChunkAccess chunk, CallbackInfo ci) {
try {
ExtendedSurfaceContext.COMPUTED_POSSIBLE_BIOMES.set(mfix$obtainBiomes(level, 1));
} catch (NoSuchElementException ignored) {
// Catch in case a biome somehow does not have a key. In that case we just don't use the computed set
}
}
}

View File

@ -1,27 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules;
import net.minecraft.world.level.levelgen.SurfaceRules;
import org.embeddedt.modernfix.world.gen.SurfaceRuleOptimizer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(targets = {"net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSource"})
public class SequenceRuleSourceMixin {
@Unique
private transient SurfaceRules.RuleSource mfix$optimizedSource;
@Inject(method = "apply(Lnet/minecraft/world/level/levelgen/SurfaceRules$Context;)Lnet/minecraft/world/level/levelgen/SurfaceRules$SurfaceRule;", at = @At("HEAD"), cancellable = true)
private void optimizeApply(SurfaceRules.Context context, CallbackInfoReturnable<SurfaceRules.SurfaceRule> cir) {
var optimizedSource = mfix$optimizedSource;
if (optimizedSource == null) {
mfix$optimizedSource = optimizedSource = SurfaceRuleOptimizer.optimizeSequenceRuleSource((SurfaceRules.SequenceRuleSource)(Object) this);
}
// Must check for it not being ourselves, to avoid infinite reentrance
if (optimizedSource != (Object)this) {
cir.setReturnValue(optimizedSource.apply(context));
}
}
}

View File

@ -0,0 +1,27 @@
package org.embeddedt.modernfix.common.mixin.perf.optimize_surface_rules;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.SurfaceRules;
import org.embeddedt.modernfix.world.gen.ExtendedSurfaceContext;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import java.util.Set;
@Mixin(SurfaceRules.Context.class)
public class SurfaceRulesContextMixin implements ExtendedSurfaceContext {
@Nullable Set<ResourceKey<Biome>> mfix$possibleBiomes;
@Override
public void mfix$applyPossibleBiomes() {
// Copy into a field so we don't hit a thread local for each BiomeConditionSource instance
this.mfix$possibleBiomes = ExtendedSurfaceContext.COMPUTED_POSSIBLE_BIOMES.get();
ExtendedSurfaceContext.COMPUTED_POSSIBLE_BIOMES.remove();
}
@Override
public @Nullable Set<ResourceKey<Biome>> mfix$getPossibleBiomes() {
return this.mfix$possibleBiomes;
}
}

View File

@ -16,6 +16,7 @@ import net.minecraft.world.level.levelgen.SurfaceRules;
import net.minecraft.world.level.levelgen.SurfaceSystem;
import net.minecraft.world.level.levelgen.WorldGenerationContext;
import org.embeddedt.modernfix.world.gen.ChunkBiomeLookup;
import org.embeddedt.modernfix.world.gen.ExtendedSurfaceContext;
import org.embeddedt.modernfix.world.gen.PrefetchingBlockColumn;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -43,6 +44,11 @@ public class SurfaceSystemMixin {
return lookup;
}
@Inject(method = "buildSurface", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/SurfaceRules$RuleSource;apply(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0))
private void injectBiomesOnContext(CallbackInfo ci, @Local(ordinal = 0) SurfaceRules.Context surfacerules$context) {
((ExtendedSurfaceContext)(Object) surfacerules$context).mfix$applyPossibleBiomes();
}
@Inject(method = "buildSurface", at = @At("TAIL"))
private void finishAndDisposeLookups(RandomState randomState, BiomeManager biomeManager, Registry<Biome> biomes, boolean p_224652_, WorldGenerationContext context, ChunkAccess chunk, NoiseChunk noiseChunk, SurfaceRules.RuleSource ruleSource, CallbackInfo ci) {
MFIX_LOOKUP_CACHE.get().dispose();

View File

@ -0,0 +1,16 @@
package org.embeddedt.modernfix.world.gen;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public interface ExtendedSurfaceContext {
ThreadLocal<Set<ResourceKey<Biome>>> COMPUTED_POSSIBLE_BIOMES = new ThreadLocal<>();
@Nullable
Set<ResourceKey<Biome>> mfix$getPossibleBiomes();
void mfix$applyPossibleBiomes();
}

View File

@ -1,144 +0,0 @@
package org.embeddedt.modernfix.world.gen;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.SurfaceRules;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class SurfaceRuleOptimizer {
public static SurfaceRules.RuleSource optimizeSequenceRuleSource(SurfaceRules.SequenceRuleSource source) {
// First pass: collect which biomes appear and count biome-gated branches
Reference2ObjectOpenHashMap<ResourceKey<Biome>, List<SurfaceRules.RuleSource>> perBiomeSources = null;
int biomeGatedBranches = 0;
var sequence = source.sequence();
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < sequence.size(); i++) {
if (sequence.get(i) instanceof SurfaceRules.TestRuleSource testRuleSource
&& testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) {
biomeGatedBranches++;
if (perBiomeSources == null) {
perBiomeSources = new Reference2ObjectOpenHashMap<>();
}
for (var biome : biomeConditionSource.biomes) {
perBiomeSources.putIfAbsent(biome, new ArrayList<>());
}
}
}
if (biomeGatedBranches < 3) {
// Just use the source as-is, not worth optimizing
return source;
}
// Second pass: build per-biome source lists preserving original interleaving order
List<SurfaceRules.RuleSource> noMatchSources = new ArrayList<>();
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < sequence.size(); i++) {
var innerSource = sequence.get(i);
if (innerSource instanceof SurfaceRules.TestRuleSource testRuleSource
&& testRuleSource.ifTrue() instanceof SurfaceRules.BiomeConditionSource biomeConditionSource) {
// Add the inner rule (condition stripped) only to the matching biomes' lists
for (var biome : biomeConditionSource.biomes) {
perBiomeSources.get(biome).add(testRuleSource.thenRun());
}
} else {
// Non-biome-gated rule: add to every biome list and the no-match list
for (var list : perBiomeSources.values()) {
list.add(innerSource);
}
noMatchSources.add(innerSource);
}
}
@SuppressWarnings("unchecked")
ResourceKey<Biome>[] biomeKeys = new ResourceKey[perBiomeSources.size()];
SurfaceRules.RuleSource[][] sourcesPerBiome = new SurfaceRules.RuleSource[perBiomeSources.size()][];
int i = 0;
for (var entry : Reference2ObjectMaps.fastIterable(perBiomeSources)) {
biomeKeys[i] = entry.getKey();
sourcesPerBiome[i] = entry.getValue().toArray(new SurfaceRules.RuleSource[0]);
i++;
}
return new OptimizedBiomeLookupSequenceRule(biomeKeys, sourcesPerBiome, noMatchSources.toArray(new SurfaceRules.RuleSource[0]));
}
public record OptimizedBiomeLookupSequenceRule(
ResourceKey<Biome>[] biomeKeys,
SurfaceRules.RuleSource[][] sourcesPerBiome,
SurfaceRules.RuleSource[] sourcesForNoBiomeMatch
) implements SurfaceRules.RuleSource {
@Override
public SurfaceRules.SurfaceRule apply(SurfaceRules.Context context) {
var biomeKeys = this.biomeKeys;
var sourcesPerBiome = this.sourcesPerBiome;
Reference2ObjectOpenHashMap<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> compiledBiomeMatch =
new Reference2ObjectOpenHashMap<>(biomeKeys.length);
for (int i = 0; i < biomeKeys.length; i++) {
var uncompiled = sourcesPerBiome[i];
SurfaceRules.SurfaceRule[] compiled = new SurfaceRules.SurfaceRule[uncompiled.length];
for (int j = 0; j < uncompiled.length; j++) {
compiled[j] = uncompiled[j].apply(context);
}
compiledBiomeMatch.put(biomeKeys[i], List.of(compiled));
}
var sourcesForNoBiomeMatch = this.sourcesForNoBiomeMatch;
List<SurfaceRules.SurfaceRule> compiledNoMatchList;
if (sourcesForNoBiomeMatch.length > 0) {
SurfaceRules.SurfaceRule[] compiledNoMatch = new SurfaceRules.SurfaceRule[sourcesForNoBiomeMatch.length];
for (int i = 0; i < sourcesForNoBiomeMatch.length; i++) {
compiledNoMatch[i] = sourcesForNoBiomeMatch[i].apply(context);
}
compiledNoMatchList = List.of(compiledNoMatch);
} else {
compiledNoMatchList = List.of();
}
return new CompiledOptimizedBiomeLookupRule(compiledBiomeMatch, compiledNoMatchList, context);
}
@Override
public KeyDispatchDataCodec<? extends SurfaceRules.RuleSource> codec() {
throw new UnsupportedOperationException("Do not try to serialize OptimizedBiomeLookupSequenceRule");
}
}
private record CompiledOptimizedBiomeLookupRule(
Map<ResourceKey<Biome>, List<SurfaceRules.SurfaceRule>> rulesForBiomeMatch,
List<SurfaceRules.SurfaceRule> rulesForNoBiomeMatch,
SurfaceRules.Context context
) implements SurfaceRules.SurfaceRule {
@Override
public @Nullable BlockState tryApply(int x, int y, int z) {
var biome = context.biome.get();
var key = (biome instanceof Holder.Reference<Biome> ref) ? ref.key() : biome.unwrapKey().orElseThrow();
var ruleList = rulesForBiomeMatch.getOrDefault(key, rulesForNoBiomeMatch);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < ruleList.size(); i++) {
var rule = ruleList.get(i);
var state = rule.tryApply(x, y, z);
if (state != null) {
return state;
}
}
return null;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
CompiledOptimizedBiomeLookupRule that = (CompiledOptimizedBiomeLookupRule) o;
return rulesForBiomeMatch.equals(that.rulesForBiomeMatch) && rulesForNoBiomeMatch.equals(that.rulesForNoBiomeMatch);
}
@Override
public int hashCode() {
return Objects.hash(rulesForBiomeMatch, rulesForNoBiomeMatch);
}
}
}