mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-19 14:59:30 +00:00
Upstream has released updates that appear to apply and compile correctly Paper Changes: PaperMC/Paper@f9edc4b Update paperweight to 1.5.11 PaperMC/Paper@2f92d4e Updated Upstream (Bukkit/CraftBukkit) PaperMC/Paper@8d8eb3b optimise explosion damage further PaperMC/Paper@7606e6d fixup tests and add missing API detected by those tests PaperMC/Paper@f17622c more cleanup and resource pack api fixes PaperMC/Paper@9051fc3 remove redundant patch PaperMC/Paper@abfb6b2 more work on adventure codecs PaperMC/Paper@b42a1da Use uuid param in setResourcePack PaperMC/Paper@bcbe5dc Fix `PlayerInteractEvent#getClickedBlock()` returning wrong block in adventure mode (#10019)
1018 lines
46 KiB
Diff
1018 lines
46 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Samsuik <40902469+Samsuik@users.noreply.github.com>
|
|
Date: Thu, 12 Oct 2023 16:23:31 +0100
|
|
Subject: [PATCH] Optimised Explosions
|
|
|
|
|
|
diff --git a/src/main/java/me/samsuik/sakura/explosion/DensityCache.java b/src/main/java/me/samsuik/sakura/explosion/DensityCache.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3f6f34cc617efaad420485a7f613cfcad88e3783
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/DensityCache.java
|
|
@@ -0,0 +1,130 @@
|
|
+package me.samsuik.sakura.explosion;
|
|
+
|
|
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+
|
|
+import javax.annotation.Nullable;
|
|
+
|
|
+public class DensityCache {
|
|
+
|
|
+ private final Int2ObjectMap<Density> densityMap = new Int2ObjectOpenHashMap<>();
|
|
+
|
|
+ public @Nullable Density retrieveCache(int key) {
|
|
+ return densityMap.get(key);
|
|
+ }
|
|
+
|
|
+ public void createCache(int key, Entity entity, Vec3 vec3d, float density) {
|
|
+ densityMap.put(key, new Density(entity.getBoundingBox(), vec3d, density));
|
|
+ }
|
|
+
|
|
+ public void clear() {
|
|
+ densityMap.clear();
|
|
+ }
|
|
+
|
|
+ public static int createKey(Entity entity, Vec3 vec3d) {
|
|
+ int hash = Mth.floor(vec3d.x);
|
|
+ hash = 31 * hash ^ Mth.floor(vec3d.y);
|
|
+ hash = 31 * hash ^ Mth.floor(vec3d.z);
|
|
+ hash = 31 * hash ^ Mth.floor(entity.getX());
|
|
+ hash = 31 * hash ^ Mth.floor(entity.getY());
|
|
+ hash = 31 * hash ^ Mth.floor(entity.getZ());
|
|
+ return hash;
|
|
+ }
|
|
+
|
|
+ public static final class Density {
|
|
+ private AABB source;
|
|
+ private AABB entity;
|
|
+ private AABB obstruction;
|
|
+ private final float density;
|
|
+ private final boolean expand;
|
|
+
|
|
+ Density(AABB bb, Vec3 p, float d) {
|
|
+ entity = bb;
|
|
+ source = new AABB(p, p);
|
|
+ density = d;
|
|
+ expand = density == 0.0f || density == 1.0f;
|
|
+ }
|
|
+
|
|
+ public boolean isExpandable() {
|
|
+ return expand;
|
|
+ }
|
|
+
|
|
+ public float density() {
|
|
+ return density;
|
|
+ }
|
|
+
|
|
+ public void obscure(Vec3 p) {
|
|
+ if (obstruction == null) {
|
|
+ obstruction = new AABB(p, p);
|
|
+ } else {
|
|
+ obstruction = expandBBWithVec3(p, obstruction);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean isObscured(Vec3 p) {
|
|
+ return obstruction != null
|
|
+ && obstruction.minX <= p.x && obstruction.maxX >= p.x
|
|
+ && obstruction.minY <= p.y && obstruction.maxY >= p.y
|
|
+ && obstruction.minZ <= p.z && obstruction.maxZ >= p.z;
|
|
+ }
|
|
+
|
|
+ public void expand(AABB bb, Vec3 p) {
|
|
+ entity = entity.minmax(bb);
|
|
+ source = expandBBWithVec3(p, source);
|
|
+ }
|
|
+
|
|
+ public boolean has(AABB bb, Vec3 p) {
|
|
+ return isBBWithinBounds(bb) && isPointWithinBounds(p);
|
|
+ }
|
|
+
|
|
+ public boolean has(Vec3 p) {
|
|
+ return isPointWithinBounds(p);
|
|
+ }
|
|
+
|
|
+ private boolean isBBWithinBounds(AABB bb) {
|
|
+ return entity.minX <= bb.minX && entity.maxX >= bb.maxX
|
|
+ && entity.minY <= bb.minY && entity.maxY >= bb.maxY
|
|
+ && entity.minZ <= bb.minZ && entity.maxZ >= bb.maxZ;
|
|
+ }
|
|
+
|
|
+ private boolean isPointWithinBounds(Vec3 p) {
|
|
+ return source.minX <= p.x && source.maxX >= p.x
|
|
+ && source.minY <= p.y && source.maxY >= p.y
|
|
+ && source.minZ <= p.z && source.maxZ >= p.z;
|
|
+ }
|
|
+
|
|
+ private AABB expandBBWithVec3(Vec3 point, AABB what) {
|
|
+ double minX = what.minX;
|
|
+ double minY = what.minY;
|
|
+ double minZ = what.minZ;
|
|
+ double maxX = what.maxX;
|
|
+ double maxY = what.maxY;
|
|
+ double maxZ = what.maxZ;
|
|
+
|
|
+ if (point.x < minX) {
|
|
+ minX = point.x;
|
|
+ } else if (point.x > maxX) {
|
|
+ maxX = point.x;
|
|
+ }
|
|
+
|
|
+ if (point.y < minY) {
|
|
+ minY = point.y;
|
|
+ } else if (point.y > maxY) {
|
|
+ maxY = point.y;
|
|
+ }
|
|
+
|
|
+ if (point.z < minZ) {
|
|
+ minZ = point.z;
|
|
+ } else if (point.z > maxZ) {
|
|
+ maxZ = point.z;
|
|
+ }
|
|
+
|
|
+ return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4f9880d35347dd008aa2ee6e67f35301ff37a4c0
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
|
|
@@ -0,0 +1,391 @@
|
|
+package me.samsuik.sakura.explosion;
|
|
+
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.particles.ParticleOptions;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.sounds.SoundEvent;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.damagesource.DamageSource;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.boss.EnderDragonPart;
|
|
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
|
+import net.minecraft.world.entity.item.PrimedTnt;
|
|
+import net.minecraft.world.entity.player.Player;
|
|
+import net.minecraft.world.item.enchantment.ProtectionEnchantment;
|
|
+import net.minecraft.world.level.Explosion;
|
|
+import net.minecraft.world.level.ExplosionDamageCalculator;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+
|
|
+/*
|
|
+ * Special explosion implementation to take advantage of TNT merging.
|
|
+ *
|
|
+ * This allows us to introduce more optimisations
|
|
+ * * if we have been unable to find any blocks nearby stop searching for blocks
|
|
+ * * avoid trying to affect entities that are completely obscured
|
|
+ * * reduce range checks for out of range entities
|
|
+ * * take better advantage of the block cache in paper
|
|
+ * * special case for explosions in the same position
|
|
+ *
|
|
+ * Unfortunately, this requires duplicating the impact entity section from Explosion.
|
|
+ *
|
|
+ * This does hurt performance in a "rogue tnt" scenario, tnt that has been spawned
|
|
+ * by an explosion destroying a tnt block often in massive blocks of tnt. It is not
|
|
+ * realistic to explode a big block of tnt in survival or factions. They only cause
|
|
+ * harm to a server and extremely wasteful for resources with minimal impact to terrain.
|
|
+ */
|
|
+public class SakuraExplosion extends Explosion {
|
|
+
|
|
+ private final Level level;
|
|
+
|
|
+ public SakuraExplosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, BlockInteraction destructionType, ParticleOptions particle, ParticleOptions emitterParticle, SoundEvent soundEvent) {
|
|
+ super(world, entity, damageSource, behavior, x, y, z, power, createFire, destructionType, particle, emitterParticle, soundEvent);
|
|
+ this.level = world;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void explode() {
|
|
+ if (this.radius < 0.1F) {
|
|
+ for (int i = 1; i < source.getStacked() && !wasCanceled; ++i) {
|
|
+ this.finalizeExplosion(false);
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ PrimedTnt origin = (PrimedTnt) source;
|
|
+ List<Vec3> positions = new ArrayList<>(origin.getStacked());
|
|
+
|
|
+ // This is a temporary entity that will be used for movement.
|
|
+ PrimedTnt tnt = new PrimedTnt(level, 0, 0, 0, null);
|
|
+ AABB bounds = new AABB(x, y, z, x, y, z);
|
|
+
|
|
+ origin.entityState().apply(tnt);
|
|
+
|
|
+ Vec3 lastMovement = tnt.getDeltaMovement();
|
|
+ ExplosionBlockCache[] blockCache = createBlockCache();
|
|
+ int wrapped = 0;
|
|
+
|
|
+ for (int i = 0; i < origin.getStacked() && !wasCanceled; ++i) {
|
|
+ if (i > 0) {
|
|
+ updatePosition(origin, tnt);
|
|
+ }
|
|
+
|
|
+ // block at explosion position
|
|
+ int blockX = Mth.floor(x);
|
|
+ int blockY = Mth.floor(y);
|
|
+ int blockZ = Mth.floor(z);
|
|
+ Vec3 position = new Vec3(x, y, z);
|
|
+
|
|
+ // search for blocks if necessary
|
|
+ if (wrapped < 7 + 12) {
|
|
+ getToBlow().clear();
|
|
+
|
|
+ long key = BlockPos.asLong(blockX, blockY, blockZ);
|
|
+ ExplosionBlockCache center = getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true);
|
|
+
|
|
+ if (interactsWithBlocks() && isDestructibleBlock(center.blockState) && isRegionUnprotected()) {
|
|
+ searchForBlocks(blockCache);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // keep track of positions and bounds
|
|
+ positions.add(position);
|
|
+ bounds = bounds.minmax(new AABB(position, position));
|
|
+
|
|
+ Vec3 movement = tnt.getDeltaMovement();
|
|
+
|
|
+ if (wrapped < 7) {
|
|
+ // Check if the explosion has wrapped around with swinging on each axis
|
|
+ if (movement.x == lastMovement.x || movement.x * lastMovement.x < 0) wrapped |= 1;
|
|
+ if (movement.y == lastMovement.y || movement.y * lastMovement.y < 0) wrapped |= 1 << 1;
|
|
+ if (movement.z == lastMovement.z || movement.z * lastMovement.z < 0) wrapped |= 1 << 2;
|
|
+ } else if (getToBlow().isEmpty() && level.sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) {
|
|
+ wrapped++;
|
|
+ } else {
|
|
+ wrapped = 7;
|
|
+ }
|
|
+
|
|
+ lastMovement = tnt.getDeltaMovement();
|
|
+
|
|
+ if (i + 1 < origin.getStacked()) {
|
|
+ BlockPos.MutableBlockPos mbp = new BlockPos.MutableBlockPos();
|
|
+ impactEntityIdle(tnt, new Entity[0], position, 1, radius * 2.0f, mbp, blockCache);
|
|
+
|
|
+ // The purpose of this is to make sure papers blockCache doesn't become
|
|
+ // outdated by flushing the map and removing stale entries from the recent
|
|
+ // cache array. If there is any case where tnt can provide its self delta
|
|
+ // movement and then start moving without blocks this may break stuff to
|
|
+ // fix it see the note above or add a boolean to mark the cache as dirty
|
|
+ // outside this loop and then invalidate before the final impact entities.
|
|
+ if (!getToBlow().isEmpty() && tnt.getDeltaMovement().lengthSqr() <= 64.0) {
|
|
+ invalidateBlockCache(blockCache);
|
|
+ }
|
|
+
|
|
+ // could it be viable to have a configuration option to only
|
|
+ // call finalize explosion when blocks are found
|
|
+ // may affect plugins that need exact explosion positions
|
|
+ super.finalizeExplosion(false);
|
|
+ ((ServerLevel) level).notifyPlayersOfExplosion(x, y, z, radius, this);
|
|
+ } else {
|
|
+ locateAndImpactEntities(positions, bounds, blockCache);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ clearBlockCache();
|
|
+ }
|
|
+
|
|
+ private void updatePosition(PrimedTnt origin, PrimedTnt tnt) {
|
|
+ boolean originMoved = !origin.position().equals(tnt.position());
|
|
+
|
|
+ origin.setFuse(100);
|
|
+ tnt.storeEntityState();
|
|
+ tnt.entityState().apply(origin);
|
|
+
|
|
+ // We have to check delta movement otherwise this optimisation can break reversing tnt.
|
|
+ // If origin was shot upwards to a block then falls in the explosion tick it will swing
|
|
+ // and origin and tnt will be in the same position every other tnt while swinging.
|
|
+ if (!getToBlow().isEmpty() || tnt.getDeltaMovement().lengthSqr() <= 64.0 || originMoved) {
|
|
+ origin.tick();
|
|
+ }
|
|
+
|
|
+ // update explosion position
|
|
+ x = origin.getX();
|
|
+ y = origin.getY(0.0625);
|
|
+ z = origin.getZ();
|
|
+ }
|
|
+
|
|
+ private void locateAndImpactEntities(List<Vec3> positions, AABB bb, ExplosionBlockCache[] blockCache) {
|
|
+ double radius = this.radius * 2.0f;
|
|
+
|
|
+ int minSection = io.papermc.paper.util.WorldUtil.getMinSection(level);
|
|
+ int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(level);
|
|
+
|
|
+ int minChunkX = Mth.floor(bb.minX - radius) >> 4;
|
|
+ int minChunkY = Mth.clamp(Mth.floor(bb.minY - radius) >> 4, minSection, maxSection);
|
|
+ int minChunkZ = Mth.floor(bb.minZ - radius) >> 4;
|
|
+ int maxChunkX = Mth.floor(bb.maxX + radius) >> 4;
|
|
+ int maxChunkY = Mth.clamp(Mth.floor(bb.maxY + radius) >> 4, minSection, maxSection);
|
|
+ int maxChunkZ = Mth.floor(bb.maxZ + radius) >> 4;
|
|
+
|
|
+ Vec3 center = bb.getCenter();
|
|
+ double change = Math.max(bb.maxX - bb.minX, Math.max(bb.maxY - bb.minY, bb.maxZ - bb.minZ));
|
|
+ double maxDistanceSqr = Math.pow(radius + change, 2);
|
|
+
|
|
+ boolean moved = change != 0.0;
|
|
+
|
|
+ BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); // Paper - optimise explosions
|
|
+
|
|
+ io.papermc.paper.chunk.system.entity.EntityLookup entityLookup = ((ServerLevel) level).getEntityLookup();
|
|
+
|
|
+ // impact entities already has a range check there is no reason to also
|
|
+ // do an intersection check when retrieving entities from the chunk.
|
|
+
|
|
+ for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
|
|
+ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
|
|
+ io.papermc.paper.world.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ);
|
|
+
|
|
+ if (chunk == null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (int chunkY = minChunkY; chunkY <= maxChunkY; ++chunkY) {
|
|
+ if (moved) {
|
|
+ impactEntities(chunk.getSectionEntities(chunkY), positions, center, blockPos, blockCache, radius, maxDistanceSqr);
|
|
+ } else {
|
|
+ impactEntitiesIdle(chunk.getSectionEntities(chunkY), positions.get(0), positions.size(), blockPos, blockCache, radius);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // swinging case: more than 1 position and actively changing positions.
|
|
+ private void impactEntities(Entity[] entities, List<Vec3> positions, Vec3 center, BlockPos.MutableBlockPos blockPos, ExplosionBlockCache[] blockCache, double radius, double maxDistanceSqr) {
|
|
+ for (int i = 0; i < entities.length; ++i) {
|
|
+ Entity entity = entities[i];
|
|
+ if (entity == null) break;
|
|
+
|
|
+ if (entity != source && !entity.ignoreExplosion(this) && entity.distanceToSqr(center.x, center.y, center.z) <= maxDistanceSqr) {
|
|
+ int key = DensityCache.createKey(entity, center);
|
|
+ DensityCache.Density data = level.densityCache.retrieveCache(key);
|
|
+ Vec3 position = entity.position();
|
|
+
|
|
+ if (data != null && data.isObscured(position)) {
|
|
+ continue;
|
|
+ } else if (impactEntity(entity, entities, positions, radius, blockPos, blockCache) == 1 && data != null) {
|
|
+ data.obscure(position);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // chunk entities can change while we're affecting entities
|
|
+ if (entities[i] != entity) {
|
|
+ i--;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private int impactEntity(Entity entity, Entity[] section, List<Vec3> positions, double radius, BlockPos.MutableBlockPos blockPos, ExplosionBlockCache[] blockCache) {
|
|
+ int found = 0;
|
|
+
|
|
+ //noinspection ForLoopReplaceableByForEach
|
|
+ for (int i = 0; i < positions.size(); i++) {
|
|
+ Vec3 pos = positions.get(i);
|
|
+
|
|
+ double distanceFromBottom = Math.sqrt(entity.distanceToSqr(pos)) / radius;
|
|
+
|
|
+ if (distanceFromBottom > 1.0) continue;
|
|
+
|
|
+ double x = entity.getX() - pos.x;
|
|
+ double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - pos.y;
|
|
+ double z = entity.getZ() - pos.z;
|
|
+ double distance = Math.sqrt(x * x + y * y + z * z);
|
|
+
|
|
+ if (distance == 0.0D) continue;
|
|
+
|
|
+ x /= distance;
|
|
+ y /= distance;
|
|
+ z /= distance;
|
|
+ double density = this.getBlockDensity(pos, entity, blockCache, blockPos); // Paper - Optimize explosions // Paper - optimise explosions
|
|
+ double exposure = (1.0D - distanceFromBottom) * density;
|
|
+
|
|
+ int visible = density != 0.0 ? 1 : 0;
|
|
+ found |= (visible << 1) | 1;
|
|
+
|
|
+ if (entity.isPrimedTNT || entity.isFallingBlock) {
|
|
+ entity.addDeltaMovement(x * exposure, y * exposure, z * exposure);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ impactNonLiving(entity, pos, section, x, y, z, exposure, blockPos, blockCache);
|
|
+ }
|
|
+
|
|
+ return found;
|
|
+ }
|
|
+
|
|
+ // stationary case: 1 position or stationary
|
|
+ private void impactEntitiesIdle(Entity[] entities, Vec3 position, int potential, BlockPos.MutableBlockPos blockPos, ExplosionBlockCache[] blockCache, double radius) {
|
|
+ for (int i = 0; i < entities.length; ++i) {
|
|
+ Entity entity = entities[i];
|
|
+ if (entity == null) break;
|
|
+
|
|
+ if (entity != source && !entity.ignoreExplosion(this)) {
|
|
+ impactEntityIdle(entity, entities, position, potential, radius, blockPos, blockCache);
|
|
+ }
|
|
+
|
|
+ // chunk entities can change while we're affecting entities
|
|
+ if (entities[i] != entity) {
|
|
+ i--;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void impactEntityIdle(Entity entity, Entity[] section, Vec3 pos, int potential, double radius, BlockPos.MutableBlockPos blockPos, ExplosionBlockCache[] blockCache) {
|
|
+ double distanceFromBottom = Math.sqrt(entity.distanceToSqr(pos)) / radius;
|
|
+
|
|
+ if (distanceFromBottom <= 1.0) {
|
|
+ double x = entity.getX() - pos.x;
|
|
+ double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - pos.y;
|
|
+ double z = entity.getZ() - pos.z;
|
|
+ double distance = Math.sqrt(x * x + y * y + z * z);
|
|
+
|
|
+ if (distance != 0.0D) {
|
|
+ x /= distance;
|
|
+ y /= distance;
|
|
+ z /= distance;
|
|
+ double density = this.getBlockDensity(pos, entity, blockCache, blockPos); // Paper - Optimize explosions // Paper - optimise explosions
|
|
+ double exposure = (1.0D - distanceFromBottom) * density;
|
|
+
|
|
+ if (entity.isPrimedTNT || entity.isFallingBlock) {
|
|
+ x *= exposure;
|
|
+ y *= exposure;
|
|
+ z *= exposure;
|
|
+
|
|
+ if (exposure == 0.0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < potential; ++i) {
|
|
+ entity.addDeltaMovement(x, y, z);
|
|
+ }
|
|
+ } else {
|
|
+ for (int i = 0; i < potential; ++i) {
|
|
+ impactNonLiving(entity, pos, section, x, y, z, exposure, blockPos, blockCache);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void impactNonLiving(Entity entity, Vec3 pos, Entity[] section, double x, double y, double z, double exposure, BlockPos.MutableBlockPos blockPos, ExplosionBlockCache[] blockCache) {
|
|
+ if (this.damageCalculator.shouldDamageEntity(this, entity)) {
|
|
+ // CraftBukkit start
|
|
+
|
|
+ // Special case ender dragon only give knockback if no damage is cancelled
|
|
+ // Thinks to note:
|
|
+ // - Setting a velocity to a ComplexEntityPart is ignored (and therefore not needed)
|
|
+ // - Damaging ComplexEntityPart while forward the damage to EntityEnderDragon
|
|
+ // - Damaging EntityEnderDragon does nothing
|
|
+ // - EntityEnderDragon hitbock always covers the other parts and is therefore always present
|
|
+ if (entity instanceof EnderDragonPart) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ CraftEventFactory.entityDamage = this.source;
|
|
+ entity.lastDamageCancelled = false;
|
|
+
|
|
+ if (entity instanceof EnderDragon) {
|
|
+ for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) {
|
|
+ for (Entity ent : section) {
|
|
+ // Calculate damage separately for each EntityComplexPart
|
|
+ if (ent == null) break;
|
|
+ if (ent == entityComplexPart) {
|
|
+ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(pos, entityComplexPart, null, blockCache, blockPos))); // Sakura // Paper - actually optimise explosions and use the right entity to calculate the damage
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(pos, entity, null, blockCache, blockPos))); // Sakura // Paper - actually optimise explosions
|
|
+ }
|
|
+
|
|
+ CraftEventFactory.entityDamage = null;
|
|
+ if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
+ double force;
|
|
+
|
|
+ if (entity instanceof LivingEntity) {
|
|
+ LivingEntity entityliving = (LivingEntity) entity;
|
|
+
|
|
+ force = entity instanceof Player && level.paperConfig().environment.disableExplosionKnockback ? 0 : ProtectionEnchantment.getExplosionKnockbackAfterDampener(entityliving, exposure); // Paper - disable explosion knockback
|
|
+ } else {
|
|
+ force = exposure;
|
|
+ }
|
|
+
|
|
+ x *= force;
|
|
+ y *= force;
|
|
+ z *= force;
|
|
+ // Sakura - moved down
|
|
+
|
|
+ entity.addDeltaMovement(x, y, z); // Sakura reduce deltamovement allocations
|
|
+ if (entity instanceof Player) {
|
|
+ Vec3 vec3d1 = new Vec3(x, y, z); // Sakura
|
|
+ Player entityhuman = (Player) entity;
|
|
+
|
|
+ if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Disable explosion knockback
|
|
+ this.getHitPlayers().put(entityhuman, vec3d1);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/me/samsuik/sakura/utils/ExplosionUtil.java b/src/main/java/me/samsuik/sakura/utils/ExplosionUtil.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e0387f16ff49031fdcbc8990613417da88d84e87
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/utils/ExplosionUtil.java
|
|
@@ -0,0 +1,64 @@
|
|
+package me.samsuik.sakura.utils;
|
|
+
|
|
+import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+
|
|
+public class ExplosionUtil {
|
|
+
|
|
+ private static final java.util.function.Function<double[], Double> highestOf = (vector) -> {
|
|
+ double highest = 0;
|
|
+
|
|
+ for (double v : vector) {
|
|
+ highest = Math.max(Math.abs(v), highest);
|
|
+ }
|
|
+
|
|
+ return highest;
|
|
+ };
|
|
+
|
|
+ public static void reduceRays(DoubleArrayList rayCoords) {
|
|
+ // temporarily transform rayCoords into a double[] list
|
|
+ List<double[]> vectors = new ArrayList<>();
|
|
+
|
|
+ for (int i = 0; i < rayCoords.size(); i += 3) {
|
|
+ vectors.add(new double[] {
|
|
+ rayCoords.getDouble(i),
|
|
+ rayCoords.getDouble(i + 1),
|
|
+ rayCoords.getDouble(i + 2)
|
|
+ });
|
|
+ }
|
|
+
|
|
+ vectors.sort((o1, o2) -> Double.compare(highestOf.apply(o2), highestOf.apply(o1)));
|
|
+
|
|
+ List<double[]> checked = new java.util.ArrayList<>();
|
|
+
|
|
+ for (double[] vector : vectors) {
|
|
+ boolean found = checked.stream().anyMatch((filtered) -> {
|
|
+ double x = (filtered[0] - vector[0]) / 0.3f;
|
|
+ double y = (filtered[1] - vector[1]) / 0.3f;
|
|
+ double z = (filtered[2] - vector[2]) / 0.3f;
|
|
+ double distanceSquared = x * x + y * y + z * z;
|
|
+
|
|
+ return (distanceSquared > 0.009 && distanceSquared < 0.01)
|
|
+ || (distanceSquared > 0.0075 && distanceSquared < 0.008)
|
|
+ || (distanceSquared > 0.006 && distanceSquared < 0.00675);
|
|
+ });
|
|
+
|
|
+ if (!found) {
|
|
+ checked.add(vector);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rayCoords.clear();
|
|
+
|
|
+ for (double[] vector : vectors) {
|
|
+ for (double coord : vector) {
|
|
+ rayCoords.add(coord);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ rayCoords.trim();
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index d131a64fe26e5ba33ccb1361c015795984fde85a..fa6da05e6d33f74bbae986c6b4db3077be432baa 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1754,6 +1754,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
worldserver.localConfig().expire(currentTickLong); // Sakura
|
|
worldserver.minimalTNT.clear(); // Sakura - visibility api
|
|
worldserver.mergeHistory.expire(currentTickLong); // Sakura - merge cannoning entities
|
|
+ worldserver.densityCache.clear(); // Sakura
|
|
}
|
|
this.isIteratingOverLevels = false; // Paper
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index a507605a04af7f576548129517029192309fd9e7..65fe46780a9f9687d328afb5cca176ab4da23318 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1961,6 +1961,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
explosion.clearToBlow();
|
|
}
|
|
|
|
+ // Sakura start
|
|
+ notifyPlayersOfExplosion(x, y, z, power, explosion);
|
|
+ return explosion;
|
|
+ }
|
|
+ public void notifyPlayersOfExplosion(double x, double y, double z, float power, Explosion explosion) {
|
|
+ // Sakura end
|
|
Iterator iterator = this.players.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -1971,7 +1977,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
|
}
|
|
}
|
|
|
|
- return explosion;
|
|
+ // Sakura - move up
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
index 826a910a02ae234012d10bb9ca3b677d127a6d13..2445bfc0c5604cb0185d240c9448a5c7fefa8784 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
@@ -86,6 +86,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
|
|| tnt.entityState().fallDistance() > 2.5f && fallDistance > 2.5f);
|
|
}
|
|
|
|
+ /*
|
|
@Override
|
|
protected void respawn() {
|
|
if (stacked <= 1) return;
|
|
@@ -123,6 +124,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
|
|
|
tnt.discard();
|
|
}
|
|
+ */
|
|
// Sakura end
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
index 3d4a75302d72bdbe47d0efbe08c89401dbe22a87..fdc0a863aaf6fee90cea28966009088a9926cb1b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Explosion.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
@@ -55,14 +55,16 @@ public class Explosion {
|
|
private final Explosion.BlockInteraction blockInteraction;
|
|
private final RandomSource random;
|
|
private final Level level;
|
|
- private final double x;
|
|
- private final double y;
|
|
- private final double z;
|
|
+ // Sakura start - expose fields
|
|
+ protected double x;
|
|
+ protected double y;
|
|
+ protected double z;
|
|
@Nullable
|
|
public final Entity source;
|
|
- private final float radius;
|
|
- private final DamageSource damageSource;
|
|
- private final ExplosionDamageCalculator damageCalculator;
|
|
+ protected final float radius;
|
|
+ protected final DamageSource damageSource;
|
|
+ protected final ExplosionDamageCalculator damageCalculator;
|
|
+ // Sakura end
|
|
private final ParticleOptions smallExplosionParticles;
|
|
private final ParticleOptions largeExplosionParticles;
|
|
private final SoundEvent explosionSound;
|
|
@@ -136,6 +138,12 @@ public class Explosion {
|
|
}
|
|
}
|
|
|
|
+ // Sakura start
|
|
+ if (me.samsuik.sakura.configuration.GlobalConfiguration.get().cannons.explosion.reducedSearchRays) {
|
|
+ me.samsuik.sakura.utils.ExplosionUtil.reduceRays(rayCoords);
|
|
+ }
|
|
+ // Sakura end
|
|
+
|
|
CACHED_RAYS = rayCoords.toDoubleArray();
|
|
}
|
|
|
|
@@ -143,14 +151,14 @@ public class Explosion {
|
|
private static final int CHUNK_CACHE_MASK = (1 << CHUNK_CACHE_SHIFT) - 1;
|
|
private static final int CHUNK_CACHE_WIDTH = 1 << CHUNK_CACHE_SHIFT;
|
|
|
|
- private static final int BLOCK_EXPLOSION_CACHE_SHIFT = 3;
|
|
- private static final int BLOCK_EXPLOSION_CACHE_MASK = (1 << BLOCK_EXPLOSION_CACHE_SHIFT) - 1;
|
|
+ protected static final int BLOCK_EXPLOSION_CACHE_SHIFT = 3; // Sakura - protected
|
|
+ protected static final int BLOCK_EXPLOSION_CACHE_MASK = (1 << BLOCK_EXPLOSION_CACHE_SHIFT) - 1; // Sakura - protected
|
|
private static final int BLOCK_EXPLOSION_CACHE_WIDTH = 1 << BLOCK_EXPLOSION_CACHE_SHIFT;
|
|
|
|
// resistance = (res + 0.3F) * 0.3F;
|
|
// so for resistance = 0, we need res = -0.3F
|
|
private static final Float ZERO_RESISTANCE = Float.valueOf(-0.3f);
|
|
- private it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<ExplosionBlockCache> blockCache = null;
|
|
+ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<ExplosionBlockCache> blockCache = null; // Sakura - protected
|
|
|
|
public static final class ExplosionBlockCache {
|
|
|
|
@@ -177,7 +185,7 @@ public class Explosion {
|
|
private long[] chunkPosCache = null;
|
|
private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null;
|
|
|
|
- private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
|
|
+ protected ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, // Sakura - private -> protected
|
|
final long key, final boolean calculateResistance) {
|
|
ExplosionBlockCache ret = this.blockCache.get(key);
|
|
if (ret != null) {
|
|
@@ -327,7 +335,8 @@ public class Explosion {
|
|
}
|
|
}
|
|
|
|
- private float getSeenFraction(final Vec3 source, final Entity target,
|
|
+ protected float getSeenFraction(final Vec3 source, final Entity target, // Sakura - protected
|
|
+ final @Nullable me.samsuik.sakura.explosion.DensityCache.Density data, // Sakura - pass density
|
|
final ExplosionBlockCache[] blockCache,
|
|
final BlockPos.MutableBlockPos blockPos) {
|
|
final AABB boundingBox = target.getBoundingBox();
|
|
@@ -365,7 +374,11 @@ public class Explosion {
|
|
Math.fma(dz, diffZ, offZ)
|
|
);
|
|
|
|
- if (!this.clipsAnything(from, source, context, blockCache, blockPos)) {
|
|
+ // Sakura start
|
|
+ if (data != null && data.isExpandable() && data.has(from)) {
|
|
+ missedRays += (int) data.density();
|
|
+ } else if (!this.clipsAnything(from, source, context, blockCache, blockPos)) {
|
|
+ // Sakura end
|
|
++missedRays;
|
|
}
|
|
}
|
|
@@ -375,12 +388,70 @@ public class Explosion {
|
|
return (float)missedRays / (float)totalRays;
|
|
}
|
|
// Paper end - optimise collisions
|
|
+ // Sakura start
|
|
+ protected ExplosionBlockCache[] createBlockCache() {
|
|
+ this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
+
|
|
+ this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
+ java.util.Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
|
|
+
|
|
+ this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
+
|
|
+ return new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
|
+ }
|
|
+
|
|
+ protected void clearBlockCache() {
|
|
+ this.blockCache = null; // Paper - optimise explosions
|
|
+ this.chunkPosCache = null; // Paper - optimise explosions
|
|
+ this.chunkCache = null; // Paper - optimise explosions
|
|
+ }
|
|
+
|
|
+ protected void invalidateBlockCache(ExplosionBlockCache[] blockCaches) {
|
|
+ for (BlockPos blow : getToBlow()) {
|
|
+ final int cacheKey =
|
|
+ (blow.getX() & BLOCK_EXPLOSION_CACHE_MASK) |
|
|
+ (blow.getY() & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
|
|
+ (blow.getZ() & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
|
+
|
|
+ blockCaches[cacheKey] = null;
|
|
+ }
|
|
+
|
|
+ blockCache.clear();
|
|
+ }
|
|
+
|
|
+ protected boolean isDestructibleBlock(@Nullable BlockState state) {
|
|
+ if (state == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ float power = radius * 1.3f;
|
|
+ float blockRes = state.getBlock().getExplosionResistance();
|
|
+ float fluidRes = state.getFluidState().getExplosionResistance();
|
|
+
|
|
+ // This should be better than just checking if we're within a fluid block.
|
|
+ return Math.max(blockRes, fluidRes) <= power;
|
|
+ }
|
|
+
|
|
+ protected boolean isRegionUnprotected() {
|
|
+ // As an optimisation, check if a plugin has cancelled or cleared the blockList.
|
|
+ // This is relatively sane on factions and cannon servers, but mileage may vary.
|
|
+ if (source != null && level.sakuraConfig().cannons.explosion.optimiseProtectedRegions) {
|
|
+ Location location = new Location(level.getWorld(), x, y, z);
|
|
+ List<org.bukkit.block.Block> blocks = new ObjectArrayList<>(1);
|
|
+ blocks.add(location.getBlock());
|
|
+ EntityExplodeEvent event = new EntityExplodeEvent(source.getBukkitEntity(), location, blocks, 0.0f);
|
|
+ return event.callEvent() && !event.blockList().isEmpty();
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ // Sakura end
|
|
|
|
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
|
|
return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity));
|
|
}
|
|
|
|
- public static float getSeenPercent(Vec3 source, Entity entity) {
|
|
+ protected static float getSeenPercent(Vec3 source, Entity entity, @Nullable me.samsuik.sakura.explosion.DensityCache.Density data) { // Sakura - protected and pass density
|
|
AABB axisalignedbb = entity.getBoundingBox();
|
|
double d0 = 1.0D / ((axisalignedbb.maxX - axisalignedbb.minX) * 2.0D + 1.0D);
|
|
double d1 = 1.0D / ((axisalignedbb.maxY - axisalignedbb.minY) * 2.0D + 1.0D);
|
|
@@ -400,7 +471,11 @@ public class Explosion {
|
|
double d10 = Mth.lerp(d7, axisalignedbb.minZ, axisalignedbb.maxZ);
|
|
Vec3 vec3d1 = new Vec3(d8 + d3, d9, d10 + d4);
|
|
|
|
- if (entity.level().clip(new ClipContext(vec3d1, source, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType() == HitResult.Type.MISS) {
|
|
+ // Sakura start
|
|
+ if (data != null && data.isExpandable() && data.has(vec3d1)) {
|
|
+ i += (int) data.density();
|
|
+ } else if (entity.level().clip(new ClipContext(vec3d1, source, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType() == HitResult.Type.MISS) {
|
|
+ // Sakura end
|
|
++i;
|
|
}
|
|
|
|
@@ -429,7 +504,29 @@ public class Explosion {
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
+ // Sakura start
|
|
+ ExplosionBlockCache[] blockCache = createBlockCache();
|
|
+
|
|
+ // block at explosion position
|
|
+ int blockX = Mth.floor(x);
|
|
+ int blockY = Mth.floor(y);
|
|
+ int blockZ = Mth.floor(z);
|
|
+ long key = BlockPos.asLong(blockX, blockY, blockZ);
|
|
+ ExplosionBlockCache center = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true);
|
|
+
|
|
+ if (interactsWithBlocks() && isDestructibleBlock(center.blockState) && isRegionUnprotected()) {
|
|
+ searchForBlocks(blockCache);
|
|
+ }
|
|
+
|
|
+ // Checking if this explosion occurred inside a block is not a viable optimisation.
|
|
+ // If an entity is 1.0e-7 away from the position no matter it will be affected no matter what.
|
|
+ locateAndImpactEntities(blockCache);
|
|
+ clearBlockCache();
|
|
+ }
|
|
+
|
|
+ protected void searchForBlocks(ExplosionBlockCache[] blockCache) {
|
|
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
|
+ // Sakura end
|
|
Set<BlockPos> set = Sets.newHashSet();
|
|
boolean flag = true;
|
|
|
|
@@ -437,14 +534,7 @@ public class Explosion {
|
|
int j;
|
|
|
|
// Paper start - optimise explosions
|
|
- this.blockCache = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
-
|
|
- this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
- java.util.Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
|
|
-
|
|
- this.chunkCache = new net.minecraft.world.level.chunk.LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
|
|
-
|
|
- final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
|
+ // Sakura - move up
|
|
// use initial cache value that is most likely to be used: the source position
|
|
final ExplosionBlockCache initialCache;
|
|
{
|
|
@@ -541,10 +631,15 @@ public class Explosion {
|
|
}
|
|
|
|
this.toBlow.addAll(set);
|
|
+ // Sakura start
|
|
+ }
|
|
+
|
|
+ protected void locateAndImpactEntities(ExplosionBlockCache[] blockCache) {
|
|
float f2 = this.radius * 2.0F;
|
|
|
|
- i = Mth.floor(this.x - (double) f2 - 1.0D);
|
|
- j = Mth.floor(this.x + (double) f2 + 1.0D);
|
|
+ int i = Mth.floor(this.x - (double) f2 - 1.0D);
|
|
+ int j = Mth.floor(this.x + (double) f2 + 1.0D);
|
|
+ // Sakura end
|
|
int l = Mth.floor(this.y - (double) f2 - 1.0D);
|
|
int i1 = Mth.floor(this.y + (double) f2 + 1.0D);
|
|
int j1 = Mth.floor(this.z - (double) f2 - 1.0D);
|
|
@@ -591,11 +686,11 @@ public class Explosion {
|
|
for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) {
|
|
// Calculate damage separately for each EntityComplexPart
|
|
if (list.contains(entityComplexPart)) {
|
|
- entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(vec3d, entityComplexPart, blockCache, blockPos))); // Paper - actually optimise explosions and use the right entity to calculate the damage
|
|
+ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getSeenFraction(vec3d, entityComplexPart, null, blockCache, blockPos))); // Sakura // Paper - actually optimise explosions and use the right entity to calculate the damage
|
|
}
|
|
}
|
|
} else {
|
|
- entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions
|
|
+ entity.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, getSeenFraction(vec3d, entity, null, blockCache, blockPos))); // Sakura // Paper - actually optimise explosions
|
|
}
|
|
|
|
CraftEventFactory.entityDamage = null;
|
|
@@ -635,9 +730,7 @@ public class Explosion {
|
|
}
|
|
}
|
|
|
|
- this.blockCache = null; // Paper - optimise explosions
|
|
- this.chunkPosCache = null; // Paper - optimise explosions
|
|
- this.chunkCache = null; // Paper - optimise explosions
|
|
+ // Sakura - move up
|
|
}
|
|
|
|
public void finalizeExplosion(boolean particles) {
|
|
@@ -705,6 +798,12 @@ public class Explosion {
|
|
if (this.wasCanceled) {
|
|
return;
|
|
}
|
|
+
|
|
+ // Sakura start
|
|
+ if (!this.level.paperConfig().environment.optimizeExplosions) {
|
|
+ this.level.densityCache.clear();
|
|
+ }
|
|
+ // Sakura end
|
|
// CraftBukkit end
|
|
objectlistiterator = this.toBlow.iterator();
|
|
|
|
@@ -851,15 +950,22 @@ public class Explosion {
|
|
private BlockInteraction() {}
|
|
}
|
|
// Paper start - Optimize explosions
|
|
- private float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions
|
|
- if (!this.level.paperConfig().environment.optimizeExplosions) {
|
|
- return this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions
|
|
+ // Sakura start
|
|
+ protected float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions
|
|
+ int key = me.samsuik.sakura.explosion.DensityCache.createKey(entity, vec3d);
|
|
+ me.samsuik.sakura.explosion.DensityCache.Density data = level.densityCache.retrieveCache(key);
|
|
+
|
|
+ if (data != null && data.has(entity.getBoundingBox(), vec3d)) {
|
|
+ return data.density();
|
|
}
|
|
- CacheKey key = new CacheKey(this, entity.getBoundingBox());
|
|
- Float blockDensity = this.level.explosionDensityCache.get(key);
|
|
- if (blockDensity == null) {
|
|
- blockDensity = this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions;
|
|
- this.level.explosionDensityCache.put(key, blockDensity);
|
|
+
|
|
+ float blockDensity = this.getSeenFraction(vec3d, entity, data, blockCache, blockPos); // Paper - optimise explosions;
|
|
+
|
|
+ if (data == null || !data.isExpandable() && (blockDensity == 0.0f || blockDensity == 1.0f)) {
|
|
+ level.densityCache.createCache(key, entity, vec3d, blockDensity);
|
|
+ } else if (data.isExpandable() && data.density() == blockDensity) {
|
|
+ data.expand(entity.getBoundingBox(), vec3d);
|
|
+ // Sakura end
|
|
}
|
|
|
|
return blockDensity;
|
|
diff --git a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
|
|
index f529f5d0f28533ec89f3ee712e59745991d068ee..d0ff7710577c1cfedae494796e6db420fef2bd08 100644
|
|
--- a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
|
|
+++ b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java
|
|
@@ -23,7 +23,7 @@ public class ExplosionDamageCalculator {
|
|
@io.papermc.paper.annotation.DoNotUse @Deprecated // Paper
|
|
public float getEntityDamageAmount(Explosion explosion, Entity entity) {
|
|
// Paper start - actually optimise explosions
|
|
- return this.getEntityDamageAmount(explosion, entity, Explosion.getSeenPercent(explosion.center(), entity));
|
|
+ return this.getEntityDamageAmount(explosion, entity, Explosion.getSeenPercent(explosion.center(), entity, null)); // Sakura
|
|
}
|
|
public float getEntityDamageAmount(Explosion explosion, Entity entity, double seenPercent) {
|
|
// Paper end - actually optimise explosions
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 37fbba2f6eb32f2806ae1fec90d7be800ba91d49..c63c5c1451d5678ddea0d570ff2628af258490ae 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -238,6 +238,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
// Sakura end
|
|
public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura
|
|
+ public final me.samsuik.sakura.explosion.DensityCache densityCache = new me.samsuik.sakura.explosion.DensityCache(); // Sakura
|
|
|
|
protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator, Supplier<me.samsuik.sakura.configuration.WorldConfiguration> sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura // Paper - Async-Anti-Xray - Pass executor
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
@@ -1431,7 +1432,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
|
}
|
|
|
|
Explosion.BlockInteraction explosion_effect1 = explosion_effect;
|
|
- Explosion explosion = new Explosion(this, entity, damageSource, behavior, x, y, z, power, createFire, explosion_effect1, particle, emitterParticle, soundEvent);
|
|
+ // Sakura start
|
|
+ Explosion explosion;
|
|
+
|
|
+ if (explosionSourceType == ExplosionInteraction.TNT) {
|
|
+ explosion = new me.samsuik.sakura.explosion.SakuraExplosion(this, entity, damageSource, behavior, x, y, z, power, createFire, explosion_effect1, particle, emitterParticle, soundEvent);
|
|
+ } else {
|
|
+ explosion = new Explosion(this, entity, damageSource, behavior, x, y, z, power, createFire, explosion_effect1, particle, emitterParticle, soundEvent);
|
|
+ }
|
|
+ // Sakura end
|
|
|
|
explosion.explode();
|
|
explosion.finalizeExplosion(particles);
|