Avoid blocking chunk generation on concentric rings calculation where possible
This commit is contained in:
parent
7c45564979
commit
b62eb1845b
|
|
@ -0,0 +1,123 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||||
|
import org.embeddedt.modernfix.annotation.FeatureLevel;
|
||||||
|
import org.embeddedt.modernfix.annotation.RequiresFeatureLevel;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
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.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(ConcentricRingsStructurePlacement.class)
|
||||||
|
@RequiresFeatureLevel(FeatureLevel.BETA)
|
||||||
|
public class ConcentricRingsStructurePlacementMixin {
|
||||||
|
|
||||||
|
@Shadow @Final private int distance;
|
||||||
|
@Shadow @Final private int spread;
|
||||||
|
@Shadow @Final private int count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum per-axis section displacement from the initial ring chunk after biome snapping.
|
||||||
|
*
|
||||||
|
* Vanilla calls findBiomeHorizontal with radius=112 blocks. In quart space this is ±28,
|
||||||
|
* and converting the selected quart back to section coordinates yields at most ±7 chunks
|
||||||
|
* per axis from the original (initialX, initialZ).
|
||||||
|
*/
|
||||||
|
@Unique private static final int MFIX_MAX_BIOME_SNAP_SECTIONS_PER_AXIS = 7;
|
||||||
|
/**
|
||||||
|
* Worst-case Euclidean error introduced by rounding:
|
||||||
|
* initialX/Z = round(cos(angle) * dist), round(sin(angle) * dist).
|
||||||
|
*/
|
||||||
|
@Unique private static final double MFIX_MAX_ROUNDING_ERROR = Math.sqrt(2.0) * 0.5;
|
||||||
|
/**
|
||||||
|
* Worst-case Euclidean biome-snap displacement when each axis can move by at most 7 chunks.
|
||||||
|
*/
|
||||||
|
@Unique private static final double MFIX_MAX_BIOME_SNAP_ERROR = MFIX_MAX_BIOME_SNAP_SECTIONS_PER_AXIS * Math.sqrt(2.0);
|
||||||
|
/**
|
||||||
|
* Total conservative positional slack (rounding + biome snap) applied to radial bounds.
|
||||||
|
*/
|
||||||
|
@Unique private static final double MFIX_MAX_POSITION_ERROR = MFIX_MAX_ROUNDING_ERROR + MFIX_MAX_BIOME_SNAP_ERROR;
|
||||||
|
|
||||||
|
/** Squared chunk-distance below which no ring position can ever land. */
|
||||||
|
@Unique private long mfix$innerRadiusSq;
|
||||||
|
/** Squared chunk-distance above which no ring position can ever land. */
|
||||||
|
@Unique private long mfix$outerRadiusSq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Precomputes conservative radial bounds for vanilla's ring placement distance:
|
||||||
|
* {@code dist = 4*i + i*i1*6 + noise}, where {@code i=distance} and {@code i1=circle}.
|
||||||
|
*
|
||||||
|
* - Inner bound uses the minimum possible base term ({@code i1=0} => {@code 4*i}).
|
||||||
|
* - Outer bound uses the maximum reachable {@code i1} for this ({@code spread,count}) pair.
|
||||||
|
*
|
||||||
|
* Both bounds are expanded by {@link #MFIX_MAX_POSITION_ERROR} so we never reject a valid
|
||||||
|
* chunk produced by rounding and biome snapping.
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
|
method = "<init>(Lnet/minecraft/core/Vec3i;Lnet/minecraft/world/level/levelgen/structure/placement/StructurePlacement$FrequencyReductionMethod;FILjava/util/Optional;IIILnet/minecraft/core/HolderSet;)V",
|
||||||
|
at = @At("RETURN")
|
||||||
|
)
|
||||||
|
private void mfix$computeRadiusBounds(CallbackInfo ci) {
|
||||||
|
double maxNoise = this.distance * 1.25; // (nextDouble() - 0.5) * (distance * 2.5)
|
||||||
|
|
||||||
|
// min(dist): 4*i + i*0*6 - maxNoise
|
||||||
|
double minDist = 4.0 * this.distance - maxNoise;
|
||||||
|
double safeInnerRadius = minDist - MFIX_MAX_POSITION_ERROR;
|
||||||
|
this.mfix$innerRadiusSq = (long)Math.max(0.0, Math.floor(safeInnerRadius * safeInnerRadius));
|
||||||
|
|
||||||
|
if (this.spread == 0) {
|
||||||
|
// Vanilla behavior becomes non-finite here (angle += 2π / 0), so keep only inner rejection.
|
||||||
|
this.mfix$outerRadiusSq = Long.MAX_VALUE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxCircle = this.mfix$computeMaxCircleIndex();
|
||||||
|
// max(dist): 4*i + i*maxCircle*6 + maxNoise
|
||||||
|
double maxDist = 4.0 * this.distance + (double)this.distance * maxCircle * 6.0 + maxNoise;
|
||||||
|
double safeOuterRadius = maxDist + MFIX_MAX_POSITION_ERROR;
|
||||||
|
this.mfix$outerRadiusSq = (long)Math.ceil(safeOuterRadius * safeOuterRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the highest ring index ({@code circle}) that vanilla can reach for this placement.
|
||||||
|
*
|
||||||
|
* This mirrors the spread/total update logic in
|
||||||
|
* {@link net.minecraft.world.level.chunk.ChunkGeneratorStructureState#generateRingPositions},
|
||||||
|
* but only tracks deterministic loop state (no RNG).
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private int mfix$computeMaxCircleIndex() {
|
||||||
|
int ringSpread = this.spread;
|
||||||
|
int total = 0;
|
||||||
|
int circle = 0;
|
||||||
|
|
||||||
|
while (total + ringSpread < this.count) {
|
||||||
|
total += ringSpread;
|
||||||
|
circle++;
|
||||||
|
ringSpread += 2 * ringSpread / (circle + 1);
|
||||||
|
ringSpread = Math.min(ringSpread, this.count - total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author embeddedt, GPT-5.3-Codex
|
||||||
|
* @reason Avoid calling getRingPositionsFor() when we know the current chunk lies outside the region where
|
||||||
|
* concentric placement can even happen. This is particularly helpful when creating new worlds, because we can
|
||||||
|
* avoid blocking on the slow noise computations within the spawn region around (0, 0).
|
||||||
|
*/
|
||||||
|
@Inject(method = "isPlacementChunk", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void mfix$earlyRejectByRadius(ChunkGeneratorStructureState structureState, int x, int z,
|
||||||
|
CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
long distSq = (long)x * x + (long)z * z;
|
||||||
|
if (distSq < this.mfix$innerRadiusSq || distSq > this.mfix$outerRadiusSq) {
|
||||||
|
cir.setReturnValue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user