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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user