9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-30 04:09:09 +00:00

Clean up BlockDensityCache and replicate optimise-explosions behaviour

This commit is contained in:
Samsuik
2025-09-25 13:21:15 +01:00
parent 8d3126aa11
commit 6e5f86e665
11 changed files with 223 additions and 102 deletions

View File

@@ -1,62 +1,85 @@
package me.samsuik.sakura.explosion.density;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.util.Mth;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* This is a replacement for papers explosion density cache to be more lenient and efficient.
*/
@NullMarked
public final class BlockDensityCache {
public static final float UNKNOWN_DENSITY = -1.0f;
private final Int2ObjectOpenHashMap<DensityData> densityDataMap = new Int2ObjectOpenHashMap<>();
private DensityData data;
private int key;
private final Object2FloatOpenHashMap<BlockDensityCacheKey> paperExactPosDensityCache = new Object2FloatOpenHashMap<>();
private final Int2ObjectOpenHashMap<CachedBlockDensity> lenientDensityCache = new Int2ObjectOpenHashMap<>();
private @Nullable BlockDensityCacheKey densityCacheKey;
private @Nullable CachedBlockDensity cacheInUse;
private int cacheKeyInUse;
private boolean knownSource;
private final Level level;
public float getDensity(Vec3 explosion, Entity entity) {
int key = getKey(explosion, entity);
DensityData data = this.densityDataMap.get(key);
public BlockDensityCache(final Level level) {
this.level = level;
this.paperExactPosDensityCache.defaultReturnValue(UNKNOWN_DENSITY);
}
if (data != null && data.hasPosition(explosion, entity.getBoundingBox())) {
return data.density();
} else {
this.knownSource = data != null && data.complete() && data.isExplosionPosition(explosion);
this.data = data;
this.key = key;
return UNKNOWN_DENSITY;
public float getBlockDensity(final Vec3 explosionPos, final Entity entity) {
final int lenientKey = BlockDensityCacheKey.getLenientKey(explosionPos, entity.blockPosition());
final CachedBlockDensity cache = this.lenientDensityCache.get(lenientKey);
// Check if the density is cached
if (cache != null && cache.hasPosition(explosionPos, entity.getBoundingBox())) {
return cache.blockDensity();
}
// Replicate the broken behaviour of optimize-explosions
if (this.level.paperConfig().environment.optimizeExplosions) {
final BlockDensityCacheKey cacheKey = new BlockDensityCacheKey(explosionPos, entity);
final float blockDensity = this.paperExactPosDensityCache.getFloat(cacheKey);
if (blockDensity != UNKNOWN_DENSITY) {
return blockDensity;
}
this.densityCacheKey = cacheKey;
}
this.knownSource = cache != null && cache.complete() && cache.isExplosionPosition(explosionPos);
this.cacheInUse = cache;
this.cacheKeyInUse = lenientKey;
return UNKNOWN_DENSITY;
}
public float getKnownDensity(final Vec3 point) {
return this.knownSource && this.cacheInUse.isKnownPosition(point)
? this.cacheInUse.blockDensity()
: UNKNOWN_DENSITY;
}
public void putDensity(final Vec3 explosionPos, final Entity entity, final float blockDensity) {
final CachedBlockDensity cache = this.cacheInUse;
if (cache == null || !cache.complete()) {
this.lenientDensityCache.put(this.cacheKeyInUse, new CachedBlockDensity(explosionPos, entity, blockDensity));
} else if (cache.blockDensity() == blockDensity) {
cache.expand(explosionPos, entity);
}
if (this.level.paperConfig().environment.optimizeExplosions && this.densityCacheKey != null) {
this.paperExactPosDensityCache.put(this.densityCacheKey, blockDensity);
}
}
public float getKnownDensity(Vec3 point) {
if (this.knownSource && this.data.isKnownPosition(point)) {
return this.data.density();
} else {
return UNKNOWN_DENSITY;
}
}
public void expire(final long tick) {
this.invalidate();
public void putDensity(Vec3 explosion, Entity entity, float density) {
if (this.data == null || !this.data.complete()) {
this.densityDataMap.put(this.key, new DensityData(explosion, entity, density));
} else if (this.data.density() == density) {
this.data.expand(explosion, entity);
if (tick % 600 == 0) {
// Trim everything down every 600 ticks
this.paperExactPosDensityCache.trim(0);
this.lenientDensityCache.trim(0);
}
}
public void invalidate() {
this.densityDataMap.clear();
}
private static int getKey(Vec3 explosion, Entity entity) {
int key = Mth.floor(explosion.x());
key = 31 * key + Mth.floor(explosion.y());
key = 31 * key + Mth.floor(explosion.z());
key = 31 * key + entity.getBlockX();
key = 31 * key + entity.getBlockY();
key = 31 * key + entity.getBlockZ();
return key;
this.lenientDensityCache.clear();
}
}

View File

@@ -0,0 +1,24 @@
package me.samsuik.sakura.explosion.density;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.NullMarked;
@NullMarked
public record BlockDensityCacheKey(Vec3 explosionPos, Vec3 entityPos) {
public BlockDensityCacheKey(final Vec3 explosionPos, final Entity entity) {
this(explosionPos, entity.position());
}
public static int getLenientKey(final Vec3 explosionPos, final BlockPos entityBlockPos) {
int key = Mth.floor(explosionPos.x());
key = 31 * key + Mth.floor(explosionPos.y());
key = 31 * key + Mth.floor(explosionPos.z());
key = 31 * key + entityBlockPos.getX();
key = 31 * key + entityBlockPos.getY();
key = 31 * key + entityBlockPos.getZ();
return key;
}
}

View File

@@ -0,0 +1,46 @@
package me.samsuik.sakura.explosion.density;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class CachedBlockDensity {
private AABB source;
private AABB entity;
private final float blockDensity;
private final boolean complete;
public CachedBlockDensity(final Vec3 explosionPos, final Entity entity, final float blockDensity) {
this.source = new AABB(explosionPos, explosionPos);
this.entity = entity.getBoundingBox();
this.blockDensity = blockDensity;
this.complete = blockDensity == 0.0f || blockDensity == 1.0f;
}
public float blockDensity() {
return this.blockDensity;
}
public boolean complete() {
return this.complete;
}
public boolean hasPosition(final Vec3 explosionPos, final AABB entityBoundingBox) {
return this.isExplosionPosition(explosionPos) && this.entity.containsInclusive(entityBoundingBox);
}
public boolean isKnownPosition(final Vec3 point) {
return this.entity.containsInclusive(point);
}
public boolean isExplosionPosition(final Vec3 explosionPos) {
return this.source.containsInclusive(explosionPos);
}
public void expand(final Vec3 explosionPos, final Entity entity) {
this.source = this.source.expand(explosionPos);
this.entity = this.entity.minmax(entity.getBoundingBox());
}
}

View File

@@ -1,47 +0,0 @@
package me.samsuik.sakura.explosion.density;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public final class DensityData {
private AABB source;
private AABB known;
private AABB entity;
private final float density;
private final boolean complete;
public DensityData(Vec3 explosion, Entity entity, float density) {
this.source = new AABB(explosion, explosion);
this.known = new AABB(entity.position(), entity.position());
this.entity = entity.getBoundingBox();
this.density = density;
this.complete = Math.abs(density - 0.5f) == 0.5f;
}
public float density() {
return this.density;
}
public boolean complete() {
return this.complete;
}
public boolean hasPosition(Vec3 explosion, AABB entity) {
return this.isExplosionPosition(explosion) && this.entity.containsInclusive(entity);
}
public boolean isKnownPosition(Vec3 point) {
return this.entity.containsInclusive(point);
}
public boolean isExplosionPosition(Vec3 explosion) {
return this.source.containsInclusive(explosion);
}
public void expand(Vec3 explosion, Entity entity) {
this.source = this.source.expand(explosion);
this.known = this.known.expand(entity.position());
this.entity = this.entity.minmax(entity.getBoundingBox());
}
}