diff --git a/patches/server/0006-Optimise-rayTracing.patch b/patches/server/0006-Optimise-rayTracing.patch index ebcef4c..048ba67 100644 --- a/patches/server/0006-Optimise-rayTracing.patch +++ b/patches/server/0006-Optimise-rayTracing.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise rayTracing diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 90a82bd7977ebe520bdcc2ab99e11452d5cf4a21..0e20d82c11702396306c0529f92496073d986380 100644 +index 90a82bd7977ebe520bdcc2ab99e11452d5cf4a21..eeacf65ebb0ea7070de6e773d31651e4c190d592 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -295,7 +295,7 @@ public class Explosion { @@ -13,7 +13,7 @@ index 90a82bd7977ebe520bdcc2ab99e11452d5cf4a21..0e20d82c11702396306c0529f9249607 } - if (!collision.isEmpty() && collision.clip(from, to, currPos) != null) { -+ if (!collision.isEmpty() && collision.clipDirect(from, to, currPos)) { // Sakura ++ if (!collision.isEmpty() && collision.clipDirect(from, to, currPos)) { // Sakura - optimise block clipping return true; } } diff --git a/patches/server/0007-Reduce-deltaMovement-Allocations.patch b/patches/server/0007-Reduce-deltaMovement-Allocations.patch index 4746255..00df522 100644 --- a/patches/server/0007-Reduce-deltaMovement-Allocations.patch +++ b/patches/server/0007-Reduce-deltaMovement-Allocations.patch @@ -214,7 +214,7 @@ index 2e6691b86e161616bb2dcf5ce0391ad57a3ef422..7890f84d7a69e2e6820ef0daa35f8985 int i = this.getFuse() - 1; diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 0e20d82c11702396306c0529f92496073d986380..89942b02225a116ec883ff8a3872d4d2ca348d17 100644 +index eeacf65ebb0ea7070de6e773d31651e4c190d592..8fd87dc8f925a160bf390b05040110735f2a145b 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -617,10 +617,11 @@ public class Explosion { @@ -226,7 +226,7 @@ index 0e20d82c11702396306c0529f92496073d986380..89942b02225a116ec883ff8a3872d4d2 // CraftBukkit start - Call EntityKnockbackEvent if (entity instanceof LivingEntity) { -+ vec3d1 = new Vec3(d8, d9, d10); // Sakura ++ vec3d1 = new Vec3(d8, d9, d10); // Sakura - reduce deltaMovement allocations Vec3 result = entity.getDeltaMovement().add(vec3d1); org.bukkit.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, org.bukkit.event.entity.EntityKnockbackEvent.KnockbackCause.EXPLOSION, d13, vec3d1, result.x, result.y, result.z); @@ -234,11 +234,11 @@ index 0e20d82c11702396306c0529f92496073d986380..89942b02225a116ec883ff8a3872d4d2 vec3d1 = org.bukkit.craftbukkit.util.CraftVector.toNMS(paperEvent.getAcceleration()); } // Paper end - call EntityKnockbackByEntityEvent for explosions -+ // Sakura start - reduce deltamovement allocations ++ // Sakura start - reduce deltaMovement allocations + entity.setDeltaMovement(entity.getDeltaMovement().add(vec3d1)); + } else { + entity.addDeltaMovement(d8, d9, d10); -+ // Sakura end - reduce deltamovement allocations ++ // Sakura end - reduce deltaMovement allocations } // CraftBukkit end - entity.setDeltaMovement(entity.getDeltaMovement().add(vec3d1)); diff --git a/patches/server/0010-TPS-Graph-Command.patch b/patches/server/0010-TPS-Graph-Command.patch index b2ddf14..b1d4e39 100644 --- a/patches/server/0010-TPS-Graph-Command.patch +++ b/patches/server/0010-TPS-Graph-Command.patch @@ -360,7 +360,7 @@ index 0000000000000000000000000000000000000000..fa5d0c046d979901dd302ee3973df274 +} diff --git a/src/main/java/me/samsuik/sakura/utils/tps/TickTracking.java b/src/main/java/me/samsuik/sakura/utils/tps/TickTracking.java new file mode 100644 -index 0000000000000000000000000000000000000000..59765bb2f4ca16421027bdd0c3e03d890a90d3e1 +index 0000000000000000000000000000000000000000..6903863ad293a335a8ed1aeaa06fccb408593373 --- /dev/null +++ b/src/main/java/me/samsuik/sakura/utils/tps/TickTracking.java @@ -0,0 +1,53 @@ @@ -399,7 +399,7 @@ index 0000000000000000000000000000000000000000..59765bb2f4ca16421027bdd0c3e03d89 + public void secondSample(Collection server, double tps) { + history.remove(history.size() - 1); + -+ int entities = server.stream().mapToInt((world) -> world.entityTickList.entityCount()).sum(); ++ int entities = server.stream().mapToInt((world) -> world.entityTickList.entities.size()).sum(); + int loaded = server.stream().mapToInt((world) -> world.chunkSource.getInternallyLoadedChunks()).sum(); + + double mspt = Arrays.stream(msptSamples).average().orElse(0.0); @@ -475,22 +475,18 @@ index 5df1d01c52b527ff74778d8e4aa27b3b0b1e7a4b..55e0be135ec084ee7ebe6bc7bb932351 private final GameEventDispatcher gameEventDispatcher; public boolean noSave; diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -index 83a39f900551e39d5af6f17a339a386ddee4feef..22782bbf76dc40d57bd6d59ff59356c07b702d8c 100644 +index 83a39f900551e39d5af6f17a339a386ddee4feef..172c8d9cc94ed6ddfd3f785ca97a6e6ebb58d2d9 100644 --- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java -@@ -11,6 +11,12 @@ import net.minecraft.world.entity.Entity; - public class EntityTickList { - private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? +@@ -9,7 +9,7 @@ import javax.annotation.Nullable; + import net.minecraft.world.entity.Entity; + + public class EntityTickList { +- private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? ++ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Sakura - tps graph // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? -+ // Sakura start -+ public int entityCount() { -+ return entities.size(); -+ } -+ // Sakura end -+ private void ensureActiveIsNotIterated() { // Paper - replace with better logic, do not delay removals - } diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java index 6c260403d91d640da0473a3df56e1c5582459fde..2d2d1eeaeb9e7f36263b8cecc753adf721b96435 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java diff --git a/patches/server/0016-Optimise-paper-explosions.patch b/patches/server/0016-Optimise-paper-explosions.patch new file mode 100644 index 0000000..f2a3e70 --- /dev/null +++ b/patches/server/0016-Optimise-paper-explosions.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 19 Apr 2024 22:20:03 +0100 +Subject: [PATCH] Optimise paper explosions + + +diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java +index 8fd87dc8f925a160bf390b05040110735f2a145b..e5335d64799005a4cdd2cb11cef637f250880b0c 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -111,6 +111,41 @@ public class Explosion { + this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit + } + ++ // Sakura start - optimise paper explosions ++ /* ++ * Sort the explosion rays to better utilise the chunk and block cache. ++ * x + Vanilla Sorted ++ * z @ z 8 5 ++ * - x 6 7 6 4 ++ * 4 @ 5 7 @ 3 ++ * 2 3 8 2 ++ * 1 1 ++ */ ++ private static final java.util.Comparator SORT_EXPLOSION_RAYS = java.util.Comparator.comparingDouble(vec -> { ++ double sign = Math.signum(vec[0]); ++ double dir = (sign - 1) / 2; ++ return sign + 8 + vec[2] * dir; ++ }); ++ ++ private static double[] sortExplosionRays(final it.unimi.dsi.fastutil.doubles.DoubleArrayList rayCoords) { ++ List explosionRays = new ArrayList<>(); ++ ++ for (int i = 0; i < rayCoords.size(); i += 3) { ++ double[] ray = new double[3]; ++ rayCoords.getElements(i, ray, 0, 3); ++ explosionRays.add(ray); ++ } ++ ++ rayCoords.clear(); ++ explosionRays.sort(SORT_EXPLOSION_RAYS); ++ ++ double[] rays = new double[explosionRays.size() * 3]; ++ for (int i = 0; i < explosionRays.size() * 3; i++) { ++ rays[i] = explosionRays.get(i / 3)[i % 3]; ++ } ++ return rays; ++ } ++ // Sakura end - optimise paper explosions + // Paper start - optimise collisions + private static final double[] CACHED_RAYS; + static { +@@ -136,7 +171,7 @@ public class Explosion { + } + } + +- CACHED_RAYS = rayCoords.toDoubleArray(); ++ CACHED_RAYS = sortExplosionRays(rayCoords); // Sakura - optimise paper explosions + } + + private static final int CHUNK_CACHE_SHIFT = 2; +@@ -430,7 +465,7 @@ public class Explosion { + } + // CraftBukkit end + this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z)); +- Set set = Sets.newHashSet(); ++ // Sakura - moved into searchForBlocks + boolean flag = true; + + int i; +@@ -456,6 +491,17 @@ public class Explosion { + + initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); + } ++ // Sakura start - optimise paper explosions ++ if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks()) { ++ this.searchForBlocks(blockCache, initialCache); ++ } ++ this.locateAndImpactEntities(blockCache); ++ this.clearBlockCache(); ++ } ++ ++ protected final void searchForBlocks(final ExplosionBlockCache[] blockCache, final ExplosionBlockCache initialCache) { ++ Set set = Sets.newHashSet(); ++ // Sakura end - optimise paper explosions + // only ~1/3rd of the loop iterations in vanilla will result in a ray, as it is iterating the perimeter of + // a 16x16x16 cube + // we can cache the rays and their normals as well, so that we eliminate the excess iterations / checks and +@@ -541,23 +587,63 @@ public class Explosion { + } + + this.toBlow.addAll(set); +- float f2 = this.radius * 2.0F; ++ // Sakura start - optimise paper explosions ++ } + +- i = Mth.floor(this.x - (double) f2 - 1.0D); +- j = Mth.floor(this.x + (double) f2 + 1.0D); ++ protected final AABB getExplosionBounds(float f2) { ++ // Sakura - moved into locateAndImpactEntities ++ ++ int i = Mth.floor(this.x - (double) f2 - 1.0D); ++ int j = Mth.floor(this.x + (double) f2 + 1.0D); + 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); + int k1 = Mth.floor(this.z + (double) f2 + 1.0D); +- List list = this.level.getEntities(this.source, new AABB((double) i, (double) l, (double) j1, (double) j, (double) i1, (double) k1), (com.google.common.base.Predicate) entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities +- Vec3 vec3d = new Vec3(this.x, this.y, this.z); +- Iterator iterator = list.iterator(); ++ return new AABB((double) i, (double) l, (double) j1, (double) j, (double) i1, (double) k1); ++ } + ++ protected final void locateAndImpactEntities(final ExplosionBlockCache[] blockCache) { ++ float f2 = this.radius * 2.0F; ++ ++ Vec3 vec3d = new Vec3(this.x, this.y, this.z); + final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); // Paper - optimise explosions + +- while (iterator.hasNext()) { +- Entity entity = (Entity) iterator.next(); ++ int minSection = io.papermc.paper.util.WorldUtil.getMinSection(this.level); ++ int maxSection = io.papermc.paper.util.WorldUtil.getMaxSection(this.level); ++ ++ int minChunkX = Mth.floor(this.x - f2) >> 4; ++ int maxChunkX = Mth.floor(this.x + f2) >> 4; ++ int minChunkY = Mth.clamp(Mth.floor(this.y - f2) >> 4, minSection, maxSection); ++ int maxChunkY = Mth.clamp(Mth.floor(this.y + f2) >> 4, minSection, maxSection); ++ int minChunkZ = Mth.floor(this.z - f2) >> 4; ++ int maxChunkZ = Mth.floor(this.z + f2) >> 4; + ++ io.papermc.paper.chunk.system.entity.EntityLookup entityLookup = ((net.minecraft.server.level.ServerLevel) this.level).getEntityLookup(); ++ ++ 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; // empty slice ++ ++ for (int chunkY = minChunkY; chunkY <= maxChunkY; ++chunkY) { ++ this.impactEntities(blockCache, blockPos, f2, vec3d, chunk.getSectionEntities(chunkY)); ++ } ++ } ++ } ++ } ++ ++ protected final void impactEntities(final ExplosionBlockCache[] blockCache, final BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity[] entities) { ++ for (int i = 0; i < entities.length; i++) { ++ Entity entity = entities[i]; ++ if (entity == null) break; // end of entity section ++ this.impactEntity(blockCache, blockPos, f2, vec3d, entity); ++ if (entity != entities[i]) i--; // entities can be removed mid-explosion ++ } ++ } ++ ++ protected final void impactEntity(final ExplosionBlockCache[] blockCache, final BlockPos.MutableBlockPos blockPos, float f2, Vec3 vec3d, Entity entity) { ++ if (entity.isAlive() && !entity.isSpectator()) { // Paper - Fix lag from explosions processing dead entities ++ // Sakura end - optimise paper explosions + if (!entity.ignoreExplosion(this)) { + double d7 = Math.sqrt(entity.distanceToSqr(vec3d)) / (double) f2; + +@@ -581,24 +667,27 @@ public class Explosion { + // - Damaging EntityEnderDragon does nothing + // - EntityEnderDragon hitbock always covers the other parts and is therefore always present + if (entity instanceof EnderDragonPart) { +- continue; ++ return; // Sakura - optimise paper explosions + } + + entity.lastDamageCancelled = false; + + if (entity instanceof EnderDragon) { ++ // Sakura start - optimise paper explosions ++ AABB bounds = this.getExplosionBounds(f2); + 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 ++ if (entityComplexPart.getBoundingBox().intersects(bounds)) { ++ entityComplexPart.hurt(this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entityComplexPart, getBlockDensity(vec3d, entityComplexPart, blockCache, blockPos))); // 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, getBlockDensity(vec3d, entity, blockCache, blockPos))); // Paper - actually optimise explosions ++ // Sakura end - optimise paper explosions + } + + if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled +- continue; ++ return; // Sakura - optimise paper explosions + } + // CraftBukkit end + } +@@ -631,7 +720,7 @@ public class Explosion { + final org.bukkit.entity.Entity hitBy = this.damageSource.getEntity() != null ? this.damageSource.getEntity().getBukkitEntity() : this.source.getBukkitEntity(); + com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent paperEvent = new com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent(((LivingEntity) entity).getBukkitLivingEntity(), hitBy, (float) event.getForce(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(vec3d1)); + if (!paperEvent.callEvent()) { +- continue; ++ return; // Sakura - optimise paper explosions + } + vec3d1 = org.bukkit.craftbukkit.util.CraftVector.toNMS(paperEvent.getAcceleration()); + } +@@ -653,8 +742,12 @@ public class Explosion { + } + } + } ++ // Sakura start - optimise paper explosions + } ++ } + ++ protected void clearBlockCache() { ++ // Sakura end - optimise paper explosions + this.blockCache = null; // Paper - optimise explosions + this.chunkPosCache = null; // Paper - optimise explosions + this.chunkCache = null; // Paper - optimise explosions diff --git a/patches/server/0016-Store-Entity-Data-State.patch b/patches/server/0017-Store-Entity-Data-State.patch similarity index 100% rename from patches/server/0016-Store-Entity-Data-State.patch rename to patches/server/0017-Store-Entity-Data-State.patch diff --git a/patches/server/0017-Merge-Cannon-Entities.patch b/patches/server/0018-Merge-Cannon-Entities.patch similarity index 98% rename from patches/server/0017-Merge-Cannon-Entities.patch rename to patches/server/0018-Merge-Cannon-Entities.patch index 19861f9..a0ad032 100644 --- a/patches/server/0017-Merge-Cannon-Entities.patch +++ b/patches/server/0018-Merge-Cannon-Entities.patch @@ -209,7 +209,7 @@ index 55e0be135ec084ee7ebe6bc7bb9323519e0cd864..51fa57e8b9d5c9ee563ec3608a437c69 this.guardEntityTick(this::tickNonPassenger, entity); gameprofilerfiller.pop(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index e657b5f88195f1db2cb9a9a97f255ff23992cadd..c518dc036459b925e384fed8cdf81d89b081a19e 100644 +index e657b5f88195f1db2cb9a9a97f255ff23992cadd..a81dde7251255aa0966498c1c8449492dae537a9 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -603,6 +603,117 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S @@ -305,7 +305,7 @@ index e657b5f88195f1db2cb9a9a97f255ff23992cadd..c518dc036459b925e384fed8cdf81d89 + return false; + } + -+ protected boolean isSafeToMergeInto(Entity entity) { ++ public boolean isSafeToMergeInto(Entity entity) { + return false; + } + @@ -375,7 +375,7 @@ index e657b5f88195f1db2cb9a9a97f255ff23992cadd..c518dc036459b925e384fed8cdf81d89 if (this.removalReason == null) { this.removalReason = entity_removalreason; diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 743aac4ba5d08ef3e6b67136bd4919b62411a7a0..7a1cc86185b9f4b6aa82cb2dd92500063b9f0736 100644 +index 743aac4ba5d08ef3e6b67136bd4919b62411a7a0..99a0bec7eb5be527b41248b365b037a5e42a3270 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -133,6 +133,59 @@ public class FallingBlockEntity extends Entity { @@ -389,7 +389,7 @@ index 743aac4ba5d08ef3e6b67136bd4919b62411a7a0..7a1cc86185b9f4b6aa82cb2dd9250006 + } + + @Override -+ protected boolean isSafeToMergeInto(Entity entity) { ++ public boolean isSafeToMergeInto(Entity entity) { + return entity instanceof FallingBlockEntity fbe + && fbe.blockState.equals(blockState) + && fbe.time - 1 == time; // todo: special case in case on spawn isn't used @@ -447,7 +447,7 @@ index 743aac4ba5d08ef3e6b67136bd4919b62411a7a0..7a1cc86185b9f4b6aa82cb2dd9250006 ((ServerLevel) this.level()).getChunkSource().chunkMap.broadcast(this, new ClientboundBlockUpdatePacket(blockposition, this.level().getBlockState(blockposition))); this.discard(EntityRemoveEvent.Cause.DESPAWN); 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 90f10473ae441d68333cd497c718a3c982544533..ed0234d6a2718d35af635c4b74243bb2afd40769 100644 +index 90f10473ae441d68333cd497c718a3c982544533..6e096594016be726e19b18c8a657ccbea717b926 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -72,6 +72,44 @@ public class PrimedTnt extends Entity implements TraceableEntity { @@ -461,7 +461,7 @@ index 90f10473ae441d68333cd497c718a3c982544533..ed0234d6a2718d35af635c4b74243bb2 + } + + @Override -+ protected boolean isSafeToMergeInto(Entity entity) { ++ public boolean isSafeToMergeInto(Entity entity) { + return entity instanceof PrimedTnt tnt + && tnt.getFuse() + 1 == getFuse() + // required to prevent issues with powdered snow diff --git a/patches/server/0018-Optimised-Explosions.patch b/patches/server/0018-Optimised-Explosions.patch deleted file mode 100644 index 88e8bf3..0000000 --- a/patches/server/0018-Optimised-Explosions.patch +++ /dev/null @@ -1,1028 +0,0 @@ -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..5c6d4124189d98421e2d6f351840c5d69bf2faf4 ---- /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 final class DensityCache { -+ -+ private final Int2ObjectMap 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..cecc88aa129e201ebe85f7ca9cfd73bc25f2f902 ---- /dev/null -+++ b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java -@@ -0,0 +1,405 @@ -+package me.samsuik.sakura.explosion; -+ -+import it.unimi.dsi.fastutil.objects.ObjectArrayList; -+import me.samsuik.sakura.entity.EntityState; -+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.EntityType; -+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 final 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(); ++i) { -+ getToBlow().clear(); -+ ((ServerLevel) level).notifyPlayersOfExplosion(x, y, z, radius, this); -+ super.finalizeExplosion(false); -+ } -+ -+ return; -+ } -+ -+ List positions = new ArrayList<>(source.getStacked()); -+ ExplosionBlockCache[] blockCache = createBlockCache(); -+ -+ EntityState entityState = null; -+ AABB bounds = new AABB(x, y, z, x, y, z); -+ Vec3 lastMovement = source.entityState().momentum(); -+ int wrapped = 0; -+ -+ for (int i = 0; i < source.getStacked() && !wasCanceled; ++i) { -+ if (i > 0) { -+ calculateNextPosition(entityState); -+ getToBlow().clear(); -+ } -+ -+ // keep track of positions and bounds -+ Vec3 position = new Vec3(x, y, z); -+ positions.add(position); -+ bounds = bounds.minmax(new AABB(position, position)); -+ -+ // search for blocks if necessary -+ if (wrapped < 7 + 12) { -+ int blockX = Mth.floor(x); -+ int blockY = Mth.floor(y); -+ int blockZ = Mth.floor(z); -+ -+ long key = BlockPos.asLong(blockX, blockY, blockZ); -+ ExplosionBlockCache center = getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); -+ -+ if (interactsWithBlocks() && isDestructibleBlock(center.blockState) && isRegionUnprotected()) { -+ searchForBlocks(blockCache); -+ } -+ } -+ -+ // Check if the explosion has wrapped around with swinging on each axis -+ if (wrapped < 7) { -+ Vec3 movement = source.entityState().momentum(); -+ 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; -+ lastMovement = movement; -+ } -+ -+ boolean isFinalExplosion = i + 1 >= source.getStacked(); -+ -+ // Possible optimisation here is making our own finalize explosion for this special case. -+ // If this is after the explosion event we can take better advantage of protection plugins. -+ if (isFinalExplosion || !getToBlow().isEmpty()) { -+ locateAndImpactEntities(positions, bounds, blockCache); -+ bounds = new AABB(position, position); -+ positions.clear(); -+ } -+ -+ if (!isFinalExplosion) { -+ BlockPos.MutableBlockPos mbp = new BlockPos.MutableBlockPos(); -+ -+ // Calculate next source velocity -+ entityState = calculateNextVelocity(position, mbp, blockCache); -+ -+ // Could be viable in the future to have a configuration option to reduce explosion events -+ List foundBlocks = new ObjectArrayList<>(getToBlow()); -+ super.finalizeExplosion(false); -+ ((ServerLevel) level).notifyPlayersOfExplosion(x, y, z, radius, this); -+ -+ // Update wrapped, this is for tracking swinging and if blocks are found -+ if (wrapped >= 7) { -+ if (getToBlow().isEmpty() && level.sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) { -+ wrapped++; -+ } else { -+ wrapped = 7; -+ } -+ } -+ -+ // The purpose of this is to make sure papers blockCache doesn't become outdated -+ // by removing stale entries from the block cache map and the recent cache array. -+ if (!foundBlocks.isEmpty() && getToBlow().isEmpty()) { -+ markBlocksInCacheAsExplodable(foundBlocks); -+ } else { -+ this.blockCache.clear(); -+ } -+ -+ java.util.Arrays.fill(blockCache, null); -+ } -+ } -+ -+ clearBlockCache(); -+ } -+ -+ private void calculateNextPosition(EntityState entityState) { -+ if (source instanceof PrimedTnt tnt) { -+ tnt.setFuse(100); -+ } -+ -+ boolean moved = !source.entityState().position().equals(source.position()); -+ entityState.apply(source); -+ source.storeEntityState(); -+ -+ if (!getToBlow().isEmpty() || source.getDeltaMovement().lengthSqr() <= 65.16525625 || moved) { -+ source.tick(); -+ } -+ -+ // update explosion position -+ x = source.getX(); -+ y = source.getY(0.0625D); -+ z = source.getZ(); -+ } -+ -+ private EntityState calculateNextVelocity(Vec3 position, BlockPos.MutableBlockPos mbp, ExplosionBlockCache[] blockCache) { -+ PrimedTnt tnt = new PrimedTnt(EntityType.TNT, level); -+ source.entityState().apply(tnt); -+ impactEntityIdle(tnt, new Entity[0], position, 1, radius * 2.0f, mbp, blockCache); -+ return EntityState.of(tnt); -+ } -+ -+ private void locateAndImpactEntities(List 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 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 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; -+ } -+ -+ impactLiving(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) { -+ impactLiving(entity, pos, section, x, y, z, exposure, blockPos, blockCache); -+ } -+ } -+ } -+ } -+ } -+ -+ private void impactLiving(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; -+ } -+ -+ 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 -+ } -+ -+ 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..84e5fe09f7432cdeec846dc1e26404706f1c298c ---- /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 final class ExplosionUtil { -+ -+ private static final java.util.function.Function 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 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 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 f78881fcf186c44da243c2f31ff855b878e172cf..d130cbd0d8336a7232794bf82cb2ccc7fc6b54a5 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1749,6 +1749,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 2.5f && fallDistance > 2.5f); - } - -+ /* - @Override - protected boolean respawnMerged() { - if (stacked <= 1) return false; -@@ -108,6 +109,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - - return true; - } -+ */ - // 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 89942b02225a116ec883ff8a3872d4d2ca348d17..89cec47f8de1a1d9e98022bf59b7849a76e03e3c 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 blockCache = null; -+ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap 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,67 @@ 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 markBlocksInCacheAsExplodable(List blocks) { -+ for (BlockPos blow : blocks) { -+ ExplosionBlockCache cache = blockCache.get(blow.asLong()); -+ // May be null if the blockCache is cleared then retrieved from the recent cache -+ if (cache != null) { -+ cache.shouldExplode = null; -+ } -+ } -+ } -+ -+ 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) + 0.3f) * 0.3f <= 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 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 +468,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 +501,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 set = Sets.newHashSet(); - boolean flag = true; - -@@ -437,14 +531,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 +628,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); -@@ -590,11 +682,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 - } - - if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled -@@ -655,9 +747,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) { -@@ -725,6 +815,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(); - -@@ -871,15 +967,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 5b93c038331c1750260a42726f5bfb97998d93a9..9e2ab49ff39010fd9c53e73ea97c9933a0f28939 100644 ---- a/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java -+++ b/src/main/java/net/minecraft/world/level/ExplosionDamageCalculator.java -@@ -25,7 +25,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 9507b2419834ace4f71a781ad90284af5e4d8aa1..5b6b9de92749a5c1c5f4f52b78d0332212ef351c 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -230,6 +230,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { - - public final it.unimi.dsi.fastutil.longs.Long2IntMap minimalTNT = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); // Sakura - visibility api - public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura - cannon entity merging -+ public final me.samsuik.sakura.explosion.DensityCache densityCache = new me.samsuik.sakura.explosion.DensityCache(); // Sakura - specialised density cache for swinging explosions - - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier 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 paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura // Paper - create paper world config; Async-Anti-Xray: Pass executor - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot -@@ -1418,7 +1419,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 (entity instanceof net.minecraft.world.entity.item.PrimedTnt) { -+ 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); diff --git a/patches/server/0019-Replace-explosion-density-cache.patch b/patches/server/0019-Replace-explosion-density-cache.patch new file mode 100644 index 0000000..f0300fb --- /dev/null +++ b/patches/server/0019-Replace-explosion-density-cache.patch @@ -0,0 +1,287 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Mon, 22 Apr 2024 23:01:26 +0100 +Subject: [PATCH] Replace explosion density cache + + +diff --git a/src/main/java/me/samsuik/sakura/explosion/density/BlockDensityCache.java b/src/main/java/me/samsuik/sakura/explosion/density/BlockDensityCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b760b03f9ddc1fb2aed135105030fc1b5dc2c9c9 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/explosion/density/BlockDensityCache.java +@@ -0,0 +1,65 @@ ++package me.samsuik.sakura.explosion.density; ++ ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++import net.minecraft.util.Mth; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.phys.Vec3; ++ ++/** ++ * This is a replacement for papers explosion density cache to be more lenient and efficient. ++ */ ++public final class BlockDensityCache { ++ public static final float UNKNOWN_DENSITY = -1.0f; ++ ++ private final Int2ObjectOpenHashMap densityDataMap = new Int2ObjectOpenHashMap<>(); ++ private DensityData data; ++ private int key; ++ private boolean knownSource; ++ ++ public float getDensity(Vec3 explosion, Entity entity) { ++ int key = getKey(explosion, entity); ++ DensityData data = this.densityDataMap.get(key); ++ ++ 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 getKnownDensity(Vec3 point) { ++ if (this.knownSource && this.data.isKnownPosition(point)) { ++ return this.data.density(); ++ } else { ++ return UNKNOWN_DENSITY; ++ } ++ } ++ ++ 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); ++ } ++ } ++ ++ public void clear(long tick) { ++ // trim size every 30 seconds ++ if (tick != -1 && tick % 600 == 0) ++ this.densityDataMap.trim(); ++ 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; ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/explosion/density/DensityData.java b/src/main/java/me/samsuik/sakura/explosion/density/DensityData.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d7e24638f07f243502004970ab4ce646cbe1e436 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/explosion/density/DensityData.java +@@ -0,0 +1,47 @@ ++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.isAABBInBounds(entity); ++ } ++ ++ public boolean isKnownPosition(Vec3 point) { ++ return this.entity.isVec3InBounds(point); ++ } ++ ++ public boolean isExplosionPosition(Vec3 explosion) { ++ return this.source.isVec3InBounds(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()); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index f78881fcf186c44da243c2f31ff855b878e172cf..62d54547fc8c1a9f7b19a2020b754a189ba7c52f 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1749,6 +1749,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier 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 paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura // Paper - create paper world config; Async-Anti-Xray: Pass executor + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot +diff --git a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java b/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java +index 0d573c05f4f8838d4492f749ca473f7a9e8d60dd..88c2c2d9fbda662509b57535c718a0cb7aa72918 100644 +--- a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java +@@ -108,6 +108,11 @@ public abstract class BasePressurePlateBlock extends Block { + if (output != j) { + BlockState iblockdata1 = this.setSignalForState(state, j); + ++ // Sakura start - explosion density cache ++ if (!world.paperConfig().environment.optimizeExplosions) { ++ world.densityCache.clear(-1); ++ } ++ // Sakura end - explosion density cache + world.setBlock(pos, iblockdata1, 2); + this.updateNeighbours(world, pos); + world.setBlocksDirty(pos, state, iblockdata1); +diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java +index efd8d77a441fd334ea4c09c5e31c6d946c1ae0b7..b3ff822f97237be81edba4fc5aa90121307ede0b 100644 +--- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java +@@ -172,6 +172,11 @@ public class TripWireHookBlock extends Block { + blockposition1 = pos.relative(enumdirection, j); + Direction enumdirection1 = enumdirection.getOpposite(); + ++ // Sakura start - explosion density cache ++ if (!world.paperConfig().environment.optimizeExplosions) { ++ world.densityCache.clear(-1); ++ } ++ // Sakura end - explosion density cache + world.setBlock(blockposition1, (BlockState) iblockdata3.setValue(TripWireHookBlock.FACING, enumdirection1), 3); + TripWireHookBlock.notifyNeighbors(block, world, blockposition1, enumdirection1); + TripWireHookBlock.emitState(world, blockposition1, flag4, flag5, flag2, flag3); +diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java +index 92394960fc76886f393cba02ac33c57739a4b383..f6c420fc86c08dcc49bfd56be6c47d2b32f813b6 100644 +--- a/src/main/java/net/minecraft/world/phys/AABB.java ++++ b/src/main/java/net/minecraft/world/phys/AABB.java +@@ -502,4 +502,28 @@ public class AABB { + public static AABB ofSize(Vec3 center, double dx, double dy, double dz) { + return new AABB(center.x - dx / 2.0, center.y - dy / 2.0, center.z - dz / 2.0, center.x + dx / 2.0, center.y + dy / 2.0, center.z + dz / 2.0); + } ++ ++ // Sakura start - explosion density cache ++ public final boolean isAABBInBounds(AABB bb) { ++ return this.minX <= bb.minX && this.maxX >= bb.maxX ++ && this.minY <= bb.minY && this.maxY >= bb.maxY ++ && this.minZ <= bb.minZ && this.maxZ >= bb.maxZ; ++ } ++ ++ public final boolean isVec3InBounds(Vec3 p) { ++ return this.minX <= p.x && this.maxX >= p.x ++ && this.minY <= p.y && this.maxY >= p.y ++ && this.minZ <= p.z && this.maxZ >= p.z; ++ } ++ ++ public final AABB expand(Vec3 pos) { ++ double minX = Math.min(this.minX, pos.x); ++ double minY = Math.min(this.minY, pos.y); ++ double minZ = Math.min(this.minZ, pos.z); ++ double maxX = Math.max(this.maxX, pos.x); ++ double maxY = Math.max(this.maxY, pos.y); ++ double maxZ = Math.max(this.maxZ, pos.z); ++ return new AABB(minX, minY, minZ, maxX, maxY, maxZ); ++ } ++ // Sakura end - explosion density cache + } diff --git a/patches/server/0020-Optimise-explosions-in-protected-regions.patch b/patches/server/0020-Optimise-explosions-in-protected-regions.patch new file mode 100644 index 0000000..36daf84 --- /dev/null +++ b/patches/server/0020-Optimise-explosions-in-protected-regions.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 3 May 2024 15:18:58 +0100 +Subject: [PATCH] Optimise explosions in protected regions + + +diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java +index 55f3f6bd77976f1ba4f7ac820a0333f1df82ab4d..fa1a06e6455390c3c945d988a3d4ee9a9ea92d38 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -415,6 +415,21 @@ public class Explosion { + return (float)missedRays / (float)totalRays; + } + // Paper end - optimise collisions ++ // Sakura start - optimise protected explosions ++ protected final 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 (this.source != null && this.level.sakuraConfig().cannons.explosion.optimiseProtectedRegions) { ++ Location location = new Location(this.level.getWorld(), this.x, this.y, this.z); ++ List blocks = new ObjectArrayList<>(1); ++ blocks.add(location.getBlock()); ++ EntityExplodeEvent event = new EntityExplodeEvent(this.source.getBukkitEntity(), location, blocks, 0.0f); ++ return event.callEvent() && !event.blockList().isEmpty(); ++ } ++ ++ return true; ++ } ++ // Sakura end - optimise protected explosions + + private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) { + return (ExplosionDamageCalculator) (entity == null ? Explosion.EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(entity)); +@@ -506,7 +521,7 @@ public class Explosion { + initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); + } + // Sakura start - optimise paper explosions +- if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks()) { ++ if (initialCache.resistance <= (this.radius * 1.3f) && this.interactsWithBlocks() && this.isRegionUnprotected()) { // Sakura - optimise protected explosions + this.searchForBlocks(blockCache, initialCache); + } + this.locateAndImpactEntities(blockCache); diff --git a/patches/server/0021-Specialised-Explosions.patch b/patches/server/0021-Specialised-Explosions.patch new file mode 100644 index 0000000..4393461 --- /dev/null +++ b/patches/server/0021-Specialised-Explosions.patch @@ -0,0 +1,575 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Fri, 3 May 2024 15:04:31 +0100 +Subject: [PATCH] Specialised Explosions + + +diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +index 0fd814f1d65c111266a2b20f86561839a4cef755..932f7a0d030d2d4932e6e6d4a5805e9b683cce67 100644 +--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java ++++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +@@ -125,6 +125,12 @@ public final class IteratorSafeOrderedReferenceSet { + } + } + ++ // Sakura start - add indexOf method ++ public int indexOf(final E element) { ++ return this.indexMap.getInt(element); ++ } ++ // Sakura end - add indexOf method ++ + public boolean remove(final E element) { + final int index = this.indexMap.removeInt(element); + if (index >= 0) { +diff --git a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +new file mode 100644 +index 0000000000000000000000000000000000000000..929231896288c8355c17ba878d3dc296e9b258da +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +@@ -0,0 +1,197 @@ ++package me.samsuik.sakura.explosion.special; ++ ++import io.papermc.paper.util.WorldUtil; ++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.item.PrimedTnt; ++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.jetbrains.annotations.Nullable; ++ ++import java.util.List; ++import java.util.function.Consumer; ++ ++public abstract class SpecialisedExplosion extends Explosion { ++ private static final double ENTITY_DISPATCH_DISTANCE = Math.pow(32.0, 2.0); ++ ++ protected final ServerLevel level; ++ protected Vec3 position; ++ protected final T cause; // preferred over source ++ private Vec3 impactPosition; ++ protected ExplosionBlockCache[] recentBlockCache; ++ protected final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); ++ ++ public SpecialisedExplosion(Level world, T 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 = (ServerLevel) world; ++ this.position = new Vec3(x, y, z); ++ this.cause = entity; ++ this.impactPosition = this.position; ++ } ++ ++ protected double getExplosionOffset() { ++ return (double) this.cause.getBbHeight() * 0.0625D; ++ } ++ ++ protected abstract void startExplosion(); ++ ++ @Override ++ protected final void clearBlockCache() { ++ super.clearBlockCache(); ++ this.recentBlockCache = null; ++ } ++ ++ @Override ++ @Deprecated ++ public final void explode() { ++ if (this.radius() < 0.1F) { ++ // (radius < 0.1F) in bukkit is assumed to not be able to find any blocks or entities. ++ for (int i = 1; i < this.cause.getStacked(); ++i) { ++ this.finalizeExplosionAndParticles(); ++ } ++ } else { ++ this.recentBlockCache = this.createBlockCache(); ++ this.startExplosion(); // search for blocks, impact entities, finalise if necessary ++ this.clearBlockCache(); ++ } ++ } ++ ++ protected final boolean requiresImpactEntities() { ++ if (this.impactPosition.distanceToSqr(this.position) > ENTITY_DISPATCH_DISTANCE) { ++ this.impactPosition = this.position; ++ return true; ++ } ++ return !this.getToBlow().isEmpty(); ++ } ++ ++ protected final boolean finalizeExplosionAndParticles() { ++ this.wasCanceled = false; ++ super.finalizeExplosion(false); ++ boolean destroyedBlocks = !this.getToBlow().isEmpty(); ++ ++ if (!this.interactsWithBlocks()) { ++ this.getToBlow().clear(); // server sends block changes in the explosion packet ++ } ++ ++ if (!this.wasCanceled) { ++ this.level.notifyPlayersOfExplosion(this.x, this.y, this.z, this.radius(), this); ++ } ++ ++ this.getToBlow().clear(); ++ return destroyedBlocks; ++ } ++ ++ protected void postExplosion(List foundBlocks, boolean destroyedBlocks) { ++ // The purpose of this is to make sure papers blockCache doesn't become outdated ++ // by removing stale entries from the block cache map and the recent cache array. ++ if (!foundBlocks.isEmpty() && !destroyedBlocks) { ++ this.markBlocksInCacheAsExplodable(foundBlocks); ++ } else { ++ super.blockCache.clear(); ++ } ++ ++ java.util.Arrays.fill(this.recentBlockCache, null); ++ } ++ ++ protected final void recalculateExplosionPosition() { ++ this.recalculateExplosionPosition(this.cause); ++ } ++ ++ protected final void recalculateExplosionPosition(T entity) { ++ this.x = entity.getX(); ++ this.y = entity.getY() + this.getExplosionOffset(); ++ this.z = entity.getZ(); ++ this.position = new Vec3(this.x, this.y, this.z); ++ } ++ ++ protected final void forEachEntitySliceInBounds(AABB bb, Consumer sliceConsumer) { ++ int minSection = WorldUtil.getMinSection(this.level); ++ int maxSection = WorldUtil.getMaxSection(this.level); ++ ++ int minChunkX = Mth.floor(bb.minX) >> 4; ++ int minChunkY = Mth.clamp(Mth.floor(bb.minY) >> 4, minSection, maxSection); ++ int minChunkZ = Mth.floor(bb.minZ) >> 4; ++ int maxChunkX = Mth.floor(bb.maxX) >> 4; ++ int maxChunkY = Mth.clamp(Mth.floor(bb.maxY) >> 4, minSection, maxSection); ++ int maxChunkZ = Mth.floor(bb.maxZ) >> 4; ++ ++ io.papermc.paper.chunk.system.entity.EntityLookup entityLookup = this.level.getEntityLookup(); ++ 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) { ++ sliceConsumer.accept(chunk.getSectionEntities(chunkY)); ++ } ++ } ++ } ++ } ++ ++ protected final void impactEntitiesFromPosition(Entity[] entities, Vec3 position, int potential, double radius) { ++ for (int i = 0; i < entities.length; ++i) { ++ Entity entity = entities[i]; ++ if (entity == null) break; ++ ++ if (entity != source && !entity.ignoreExplosion(this)) { ++ this.impactEntity(entity, position, potential, radius); ++ } ++ ++ if (entities[i] != entity) { ++ i--; ++ } ++ } ++ } ++ ++ protected final void impactEntity(Entity entity, Vec3 pos, int potential, double radius) { ++ if (entity.isPrimedTNT || entity.isFallingBlock) { ++ this.impactCannonEntity(entity, pos, potential, radius); ++ } else { ++ for (int i = 0; i < potential; ++i) { ++ super.impactEntity(this.recentBlockCache, this.mutablePos, (float) radius, pos, entity); ++ } ++ } ++ } ++ ++ protected final void impactCannonEntity(Entity entity, Vec3 pos, int potential, double radius) { ++ 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()) - this.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, this.recentBlockCache, this.mutablePos); // Paper - Optimize explosions // Paper - optimise explosions ++ double exposure = (1.0D - distanceFromBottom) * density; ++ ++ x *= exposure; ++ y *= exposure; ++ z *= exposure; ++ ++ if (exposure == 0.0) { ++ return; ++ } ++ ++ for (int i = 0; i < potential; ++i) { ++ entity.addDeltaMovement(x, y, z); ++ } ++ } ++ } ++ } ++} +diff --git a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b94861d545cefb0e68a83efa6812a1ff4a381027 +--- /dev/null ++++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +@@ -0,0 +1,179 @@ ++package me.samsuik.sakura.explosion.special; ++ ++import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet; ++import it.unimi.dsi.fastutil.objects.ObjectArrayList; ++import me.samsuik.sakura.entity.EntityState; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.Direction; ++import net.minecraft.core.particles.ParticleOptions; ++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.EntityType; ++import net.minecraft.world.entity.item.PrimedTnt; ++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.jetbrains.annotations.Nullable; ++ ++import java.util.List; ++ ++public final class TntExplosion extends SpecialisedExplosion { ++ private static final int ALL_DIRECTIONS = 0b111; ++ private static final int FOUND_ALL_BLOCKS = ALL_DIRECTIONS + 12; ++ ++ private final Vec3 originalPosition; ++ private final List explosions = new ObjectArrayList<>(); ++ private AABB bounds; ++ private int wrapped = 0; ++ private int movement = 0; ++ ++ public TntExplosion(Level world, PrimedTnt tnt, @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, tnt, damageSource, behavior, x, y, z, power, createFire, destructionType, particle, emitterParticle, soundEvent); ++ this.originalPosition = this.position; ++ this.bounds = new AABB(x, y, z, x, y, z); ++ } ++ ++ @Override ++ protected void startExplosion() { ++ for (int i = this.calculateExplosionPotential() - 1; i >= 0; --i) { ++ Vec3 previousMomentum = this.cause.entityState().momentum(); ++ boolean lastCycle = (i == 0); ++ this.midExplosion(previousMomentum, lastCycle); // search for blocks and impact entities ++ ++ if (!lastCycle) { ++ EntityState entityState = this.nextSourceVelocity(); ++ List foundBlocks = new ObjectArrayList<>(this.getToBlow()); ++ boolean destroyedBlocks = this.finalizeExplosionAndParticles(); ++ this.postExplosion(foundBlocks, destroyedBlocks); // update wrapped, clear recent block cache ++ this.updateExplosionPosition(entityState, destroyedBlocks); ++ } ++ } ++ } ++ ++ private void midExplosion(Vec3 previousMomentum, boolean lastCycle) { ++ if (this.wrapped < FOUND_ALL_BLOCKS) { ++ final int blockX = Mth.floor(this.x); ++ final int blockY = Mth.floor(this.y); ++ final int blockZ = Mth.floor(this.z); ++ ++ final long key = BlockPos.asLong(blockX, blockY, blockZ); ++ ++ final ExplosionBlockCache initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true); ++ ++ if (initialCache.resistance <= (this.radius() * 1.3f) && this.interactsWithBlocks() && this.isRegionUnprotected()) { // Sakura - optimise protected explosions ++ this.searchForBlocks(this.recentBlockCache, initialCache); ++ } ++ } ++ ++ if (this.wrapped < ALL_DIRECTIONS) { ++ Vec3 momentum = this.cause.entityState().momentum(); ++ for (Direction.Axis axis : Direction.Axis.VALUES) { ++ double current = momentum.get(axis); ++ double previous = previousMomentum.get(axis); ++ if (current == previous || current * previous < 0) { ++ this.wrapped |= 1 << axis.ordinal(); ++ } ++ } ++ } ++ ++ this.bounds = this.bounds.expand(this.position); ++ this.explosions.add(this.position); ++ ++ if (lastCycle || this.requiresImpactEntities()) { ++ this.locateAndImpactEntitiesInBounds(); ++ this.bounds = new AABB(this.position, this.position); ++ this.explosions.clear(); ++ } ++ } ++ ++ @Override ++ protected void postExplosion(List foundBlocks, boolean destroyedBlocks) { ++ super.postExplosion(foundBlocks, destroyedBlocks); ++ // Update wrapped, this is for tracking swinging and if blocks are found ++ if (this.wrapped >= 7) { ++ if (!destroyedBlocks && this.level.sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) { ++ this.wrapped++; ++ } else { ++ this.wrapped = 7; ++ } ++ } ++ } ++ ++ private EntityState nextSourceVelocity() { ++ PrimedTnt tnt = new PrimedTnt(EntityType.TNT, this.level); ++ this.cause.entityState().apply(tnt); ++ this.impactCannonEntity(tnt, this.position, 1, this.radius() * 2.0f); ++ return EntityState.of(tnt); ++ } ++ ++ private void updateExplosionPosition(EntityState entityState, boolean destroyedBlocks) { ++ // Ticking is always required after destroying a block. ++ if (destroyedBlocks || !this.position.equals(this.cause.position()) && (this.movement != 1 || !this.originalPosition.equals(this.position))) { ++ entityState.apply(this.cause); ++ this.cause.storeEntityState(); ++ this.cause.setFuse(100); ++ this.cause.tick(); ++ this.movement++; ++ this.recalculateExplosionPosition(); ++ } ++ } ++ ++ private int calculateExplosionPotential() { ++ IteratorSafeOrderedReferenceSet entities = this.level.entityTickList.entities; ++ int base = this.cause.getStacked(); ++ int index = entities.indexOf(this.cause); ++ // iterate over the entityTickList to find entities that are exploding in the same position. ++ while ((index = entities.advanceRawIterator(index)) != -1) { ++ Entity foundEntity = entities.rawGet(index); ++ if (!foundEntity.compareState(this.cause) || !foundEntity.isSafeToMergeInto(this.cause)) ++ break; ++ base += foundEntity.getStacked(); ++ foundEntity.discard(); ++ } ++ ++ return base; ++ } ++ ++ private void locateAndImpactEntitiesInBounds() { ++ double radius = this.radius() * 2.0f; ++ AABB bb = this.bounds; ++ ++ Vec3 center = bb.getCenter(); ++ double change = Math.max(bb.getXsize(), Math.max(bb.getYsize(), bb.getZsize())); ++ double maxDistanceSqr = Math.pow(radius + change, 2.0); ++ boolean moved = (change != 0.0); ++ ++ this.forEachEntitySliceInBounds(bb.inflate(radius), entities -> { ++ if (moved) { ++ this.impactEntitiesSwinging(entities, center, radius, maxDistanceSqr); ++ } else { ++ this.impactEntitiesFromPosition(entities, this.explosions.get(0), this.explosions.size(), radius); ++ } ++ }); ++ } ++ ++ private void impactEntitiesSwinging(Entity[] entities, Vec3 center, double radius, double maxDistanceSqr) { ++ for (int i = 0; i < entities.length; ++i) { ++ Entity entity = entities[i]; ++ if (entity == null) break; ++ ++ if (entity != this.source && !entity.ignoreExplosion(this) && entity.distanceToSqr(center.x, center.y, center.z) <= maxDistanceSqr) { ++ this.impactEntity(entity, radius); ++ } ++ ++ if (entities[i] != entity) { ++ i--; ++ } ++ } ++ } ++ ++ private void impactEntity(Entity entity, double radius) { ++ //noinspection ForLoopReplaceableByForEach ++ for (int i = 0; i < this.explosions.size(); i++) { ++ this.impactEntity(entity, this.explosions.get(i), 1, radius); ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 51fa57e8b9d5c9ee563ec3608a437c69da08d32c..ec7d7f22b267ae6572e6005f10221755cbb1a480 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1957,6 +1957,12 @@ public class ServerLevel extends Level implements WorldGenLevel { + explosion.clearToBlow(); + } + ++ // Sakura start - specialised explosions ++ this.notifyPlayersOfExplosion(x, y, z, power, explosion); ++ return explosion; ++ } ++ public final void notifyPlayersOfExplosion(double x, double y, double z, float power, Explosion explosion) { ++ // Sakura end - specialised explosions + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { +@@ -1967,7 +1973,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + } + } + +- return explosion; ++ // Sakura - return moved up into explode + } + + @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 6e096594016be726e19b18c8a657ccbea717b926..30c3e31148d23998ce9007691c30d1d518a2b86d 100644 +--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java ++++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +@@ -86,28 +86,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { + && (tnt.entityState().fallDistance() == 0.0f && fallDistance == 0.0f + || tnt.entityState().fallDistance() > 2.5f && fallDistance > 2.5f); + } +- +- @Override +- protected boolean respawnMerged() { +- if (stacked <= 1) return false; +- +- PrimedTnt tnt = new PrimedTnt(EntityType.TNT, level()); +- +- while (stacked-- > 1) { +- this.setFuse(100); // Prevent unwanted explosions while ticking +- +- // Cause an explosion to affect this entity +- tnt.setPos(this.position()); +- tnt.setDeltaMovement(this.getDeltaMovement()); +- this.entityState().apply(this); +- tnt.explode(); +- this.storeEntityState(); +- +- this.tick(); +- } +- +- return true; +- } ++ // Sakura - specialised explosions + // 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 fa1a06e6455390c3c945d988a3d4ee9a9ea92d38..985386901b4785e83fd8b85db9e05847f9165d1e 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -55,9 +55,11 @@ 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 - private -> protected ++ protected double x; ++ protected double y; ++ protected double z; ++ // Sakura end - private -> protected + @Nullable + public final Entity source; + private final float radius; +@@ -185,7 +187,7 @@ public class Explosion { + // 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 blockCache = null; ++ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap blockCache = null; // Sakura - private -> protected + + public static final class ExplosionBlockCache { + +@@ -212,7 +214,29 @@ 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, ++ // Sakura start - specialised explosions ++ protected final 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 final void markBlocksInCacheAsExplodable(List blocks) { ++ for (BlockPos blow : blocks) { ++ ExplosionBlockCache cache = this.blockCache.get(blow.asLong()); ++ // May be null if the blockCache is cleared then retrieved from the recent block cache ++ if (cache != null) { ++ cache.shouldExplode = null; ++ } ++ } ++ } ++ // Sakura end - specialised explosions ++ protected final 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) { +@@ -501,14 +525,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]; ++ final ExplosionBlockCache[] blockCache = this.createBlockCache(); + // use initial cache value that is most likely to be used: the source position + final ExplosionBlockCache initialCache; + { +@@ -998,7 +1015,7 @@ public class Explosion { + private BlockInteraction() {} + } + // Paper start - Optimize explosions +- private float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise explosions ++ protected final float getBlockDensity(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Sakura - private -> protected // Paper - optimise explosions + // Sakura start - replace density cache + float blockDensity = this.level.densityCache.getDensity(vec3d, entity); + if (blockDensity == me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index d555ea21a6e53938003a45d5de2ce97c4986f9e7..ad4076dd0352eb4f62588e3c83ffb2c42b07a3e0 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -1419,7 +1419,14 @@ 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 - specialised explosions ++ final Explosion explosion; ++ if (entity instanceof net.minecraft.world.entity.item.PrimedTnt tnt) { ++ explosion = new me.samsuik.sakura.explosion.special.TntExplosion(this, tnt, 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 - specialised explosions + + explosion.explode(); + explosion.finalizeExplosion(particles); diff --git a/patches/server/0019-Optimise-Fast-Movement.patch b/patches/server/0022-Optimise-Fast-Movement.patch similarity index 96% rename from patches/server/0019-Optimise-Fast-Movement.patch rename to patches/server/0022-Optimise-Fast-Movement.patch index ee43ace..92dfd62 100644 --- a/patches/server/0019-Optimise-Fast-Movement.patch +++ b/patches/server/0022-Optimise-Fast-Movement.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise Fast Movement diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index c518dc036459b925e384fed8cdf81d89b081a19e..11b137cad5455a46064fafe73fd0ab60ffb537a3 100644 +index a81dde7251255aa0966498c1c8449492dae537a9..08a5558679da5602f05f2f052cb841d008d866cc 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -1236,6 +1236,95 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S @@ -201,7 +201,7 @@ index c518dc036459b925e384fed8cdf81d89b081a19e..11b137cad5455a46064fafe73fd0ab60 // Paper start - optimise collisions final boolean xZero = movement.x == 0.0; diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 7a1cc86185b9f4b6aa82cb2dd92500063b9f0736..0e60e3f694dd4d52ce92290148ff043e834d7c4c 100644 +index 99a0bec7eb5be527b41248b365b037a5e42a3270..16a8b667f50700a85f1c14f3a7ed3098e732321a 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -198,7 +198,7 @@ public class FallingBlockEntity extends Entity { @@ -214,10 +214,10 @@ index 7a1cc86185b9f4b6aa82cb2dd92500063b9f0736..0e60e3f694dd4d52ce92290148ff043e if (this.level().paperConfig().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) { if (this.dropItem && this.level().getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) { 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 2800af98260ebdab107466c596d2ec8cba6088fe..7ff11a09234606508dac8347af281885b0a1f7e1 100644 +index 30c3e31148d23998ce9007691c30d1d518a2b86d..491dab62dda6e24bda5b01364a2adbf51ad74ef6 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -119,7 +119,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -96,7 +96,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { this.addDeltaMovement(0.0D, -0.04D, 0.0D); // Sakura - reduce movement allocations } diff --git a/patches/server/0020-Limited-Get-Entities.patch b/patches/server/0023-Limited-Get-Entities.patch similarity index 98% rename from patches/server/0020-Limited-Get-Entities.patch rename to patches/server/0023-Limited-Get-Entities.patch index 1b19153..2e6dbcb 100644 --- a/patches/server/0020-Limited-Get-Entities.patch +++ b/patches/server/0023-Limited-Get-Entities.patch @@ -329,12 +329,12 @@ index 8fcaa00e461c7f4413bf655ddd8165a2b908f900..404b99def4562942e036089085a66797 if (this.count == 0) { return; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 5b6b9de92749a5c1c5f4f52b78d0332212ef351c..2ab6bfd7887cd7a8b4bb6a9b09938d9c30f1cb41 100644 +index ad4076dd0352eb4f62588e3c83ffb2c42b07a3e0..360e72b8e540f3cdb557bc59bca5e0a9cda239f8 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -232,6 +232,39 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura - cannon entity merging - public final me.samsuik.sakura.explosion.DensityCache densityCache = new me.samsuik.sakura.explosion.DensityCache(); // Sakura - specialised density cache for swinging explosions + public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache + // Sakura start - add entity retrival methods with search limits + public void getLimitedEntities(Entity except, AABB box, Predicate predicate, List into, int limit, int search) { diff --git a/patches/server/0021-isPushedByFluid-API.patch b/patches/server/0024-isPushedByFluid-API.patch similarity index 95% rename from patches/server/0021-isPushedByFluid-API.patch rename to patches/server/0024-isPushedByFluid-API.patch index 02f78d7..5484719 100644 --- a/patches/server/0021-isPushedByFluid-API.patch +++ b/patches/server/0024-isPushedByFluid-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] isPushedByFluid API diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 11b137cad5455a46064fafe73fd0ab60ffb537a3..36692db781dc7c3a1cff6302ebdd257e0075e91c 100644 +index 08a5558679da5602f05f2f052cb841d008d866cc..24dcf92c85d4453564d3ca51687a59497d8182fe 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -714,6 +714,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S diff --git a/patches/server/0022-Cannon-Mechanics.patch b/patches/server/0025-Cannon-Mechanics.patch similarity index 93% rename from patches/server/0022-Cannon-Mechanics.patch rename to patches/server/0025-Cannon-Mechanics.patch index c78fa2f..68d267c 100644 --- a/patches/server/0022-Cannon-Mechanics.patch +++ b/patches/server/0025-Cannon-Mechanics.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Cannon Mechanics diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 0e60e3f694dd4d52ce92290148ff043e834d7c4c..8ee503cb9cee4984a6fbb79e486c5e730c46e125 100644 +index 16a8b667f50700a85f1c14f3a7ed3098e732321a..fd7524b3aae54bc62dce1c54666f18ad0ea230b4 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -68,6 +68,7 @@ public class FallingBlockEntity extends Entity { @@ -38,7 +38,7 @@ index 0e60e3f694dd4d52ce92290148ff043e834d7c4c..8ee503cb9cee4984a6fbb79e486c5e73 @Override public void tick() { 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 7ff11a09234606508dac8347af281885b0a1f7e1..2123f82f881fea63cef197d91c267358c24ff126 100644 +index 491dab62dda6e24bda5b01364a2adbf51ad74ef6..403cfb14dff46a405a1476f6806020e9a2c9a941 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -54,6 +54,12 @@ public class PrimedTnt extends Entity implements TraceableEntity { @@ -54,7 +54,7 @@ index 7ff11a09234606508dac8347af281885b0a1f7e1..2123f82f881fea63cef197d91c267358 } @Override -@@ -248,7 +254,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -225,7 +231,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { diff --git a/patches/server/0023-Cache-MovingBlockEntity-collision-shape.patch b/patches/server/0026-Cache-MovingBlockEntity-collision-shape.patch similarity index 100% rename from patches/server/0023-Cache-MovingBlockEntity-collision-shape.patch rename to patches/server/0026-Cache-MovingBlockEntity-collision-shape.patch diff --git a/patches/server/0024-Optimise-TNT-fluid-state-and-pushing.patch b/patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch similarity index 84% rename from patches/server/0024-Optimise-TNT-fluid-state-and-pushing.patch rename to patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch index 319dcb7..d1dcbda 100644 --- a/patches/server/0024-Optimise-TNT-fluid-state-and-pushing.patch +++ b/patches/server/0027-Optimise-TNT-fluid-state-and-pushing.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise TNT fluid state and pushing diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 36692db781dc7c3a1cff6302ebdd257e0075e91c..07c4cb5d91d0f7549fb92bfc03d730145e63a7d5 100644 +index 24dcf92c85d4453564d3ca51687a59497d8182fe..e3b6ef30a3c983e7e9b6bd83bc7baa600b16d850 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -2199,7 +2199,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S @@ -18,12 +18,12 @@ index 36692db781dc7c3a1cff6302ebdd257e0075e91c..07c4cb5d91d0f7549fb92bfc03d73014 if (entity instanceof Boat) { 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 2123f82f881fea63cef197d91c267358c24ff126..3ebc6fabbd9e5f9e71a97fe6153f24116c66f828 100644 +index 403cfb14dff46a405a1476f6806020e9a2c9a941..b4b509bcdd470ff21bbe6e0c3f58848c168bc120 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java -@@ -117,6 +117,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -94,6 +94,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { } - */ + // Sakura - specialised explosions // Sakura end + // Sakura start + protected boolean updateInWaterStateAndDoFluidPushing() { diff --git a/patches/server/0025-Optimise-LivingEntity-pushEntities.patch b/patches/server/0028-Optimise-LivingEntity-pushEntities.patch similarity index 100% rename from patches/server/0025-Optimise-LivingEntity-pushEntities.patch rename to patches/server/0028-Optimise-LivingEntity-pushEntities.patch diff --git a/patches/server/0026-Despawn-falling-blocks-inside-moving-pistons.patch b/patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch similarity index 93% rename from patches/server/0026-Despawn-falling-blocks-inside-moving-pistons.patch rename to patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch index 90c2e1b..5afe28c 100644 --- a/patches/server/0026-Despawn-falling-blocks-inside-moving-pistons.patch +++ b/patches/server/0029-Despawn-falling-blocks-inside-moving-pistons.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Despawn falling blocks inside moving pistons diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 8ee503cb9cee4984a6fbb79e486c5e730c46e125..60d7ffebb2fa4e39d5ba0033c746e85ff4b97727 100644 +index fd7524b3aae54bc62dce1c54666f18ad0ea230b4..7bfbdfd4c61941eb6a95ceeb8face277b31735eb 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -232,7 +232,7 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0029-Destroy-Waterlogged-Blocks.patch b/patches/server/0029-Destroy-Waterlogged-Blocks.patch deleted file mode 100644 index 5035b87..0000000 --- a/patches/server/0029-Destroy-Waterlogged-Blocks.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Thu, 16 Nov 2023 00:59:04 +0000 -Subject: [PATCH] Destroy Waterlogged Blocks - - -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index a7a8ad1bd1b9117f42c67d1f35e3dbd22da67286..01165a3543a28297f609dcdfe6a60bf8191ab04e 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -210,6 +210,12 @@ public class Explosion { - BlockState blockState = chunk.getBlockStateFinal(x, y, z); - FluidState fluidState = blockState.getFluidState(); - -+ // Sakura start - destroy water logged blocks -+ if (calculateResistance && !blockState.isAir() && this.level.sakuraConfig().cannons.explosion.destroyWaterloggedBlocks) { -+ fluidState = Blocks.AIR.defaultBlockState().getFluidState(); -+ } -+ // Sakura end - destroy water logged blocks -+ - Optional resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); - - // Sakura start - explosion durable blocks diff --git a/patches/server/0027-Configure-Entity-Knockback.patch b/patches/server/0030-Configure-Entity-Knockback.patch similarity index 100% rename from patches/server/0027-Configure-Entity-Knockback.patch rename to patches/server/0030-Configure-Entity-Knockback.patch diff --git a/patches/server/0028-Explosion-Durable-Blocks.patch b/patches/server/0031-Explosion-Durable-Blocks.patch similarity index 78% rename from patches/server/0028-Explosion-Durable-Blocks.patch rename to patches/server/0031-Explosion-Durable-Blocks.patch index 4ebf9f9..363548e 100644 --- a/patches/server/0028-Explosion-Durable-Blocks.patch +++ b/patches/server/0031-Explosion-Durable-Blocks.patch @@ -74,13 +74,13 @@ index 0000000000000000000000000000000000000000..c58e52f7cc012babf4235e405e5fb501 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d130cbd0d8336a7232794bf82cb2ccc7fc6b54a5..8f5c2d979a6d902d142ba57a824beddf857f54b4 100644 +index 62d54547fc8c1a9f7b19a2020b754a189ba7c52f..622ab8f82f1b641f32912788ce44381b88f46093 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1750,6 +1750,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); +- Optional resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); ++ Optional resistance = !calculateResistance ? Optional.empty() : this.calculateBlockResistance(blockState, fluidState, pos); // Sakura - explosion durable blocks -+ // Sakura start - explosion durable blocks -+ if (calculateResistance) { -+ Block block = blockState.getBlock(); -+ me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(pos).durableMaterials.get(block); -+ -+ if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { -+ resistance = Optional.of(material.resistance()); -+ } -+ } -+ // Sakura end - explosion durable blocks -+ ret = new ExplosionBlockCache( key, pos, blockState, fluidState, - (resistance.orElse(ZERO_RESISTANCE).floatValue() + 0.3f) * 0.3f, -@@ -829,6 +840,16 @@ public class Explosion { +@@ -275,6 +275,21 @@ public class Explosion { + return ret; + } + ++ // Sakura start - explosion durable blocks ++ private Optional calculateBlockResistance(BlockState blockState, FluidState fluidState, BlockPos pos) { ++ if (!blockState.isAir()) { ++ Block block = blockState.getBlock(); ++ me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(pos).durableMaterials.get(block); ++ ++ if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { ++ return Optional.of(material.resistance()); ++ } ++ } ++ ++ return this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); ++ } ++ // Sakura end - explosion durable blocks ++ + private boolean clipsAnything(final Vec3 from, final Vec3 to, + final io.papermc.paper.util.CollisionUtil.LazyEntityCollisionContext context, + final ExplosionBlockCache[] blockCache, +@@ -872,6 +887,16 @@ public class Explosion { // CraftBukkit start - TNTPrimeEvent BlockState iblockdata = this.level.getBlockState(blockposition); Block block = iblockdata.getBlock(); @@ -163,13 +176,13 @@ index 89cec47f8de1a1d9e98022bf59b7849a76e03e3c..a7a8ad1bd1b9117f42c67d1f35e3dbd2 Entity sourceEntity = this.source == null ? null : this.source; BlockPos sourceBlock = sourceEntity == null ? BlockPos.containing(this.x, this.y, this.z) : null; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 2ab6bfd7887cd7a8b4bb6a9b09938d9c30f1cb41..fb0b9b33494cc1f4fa45709ee97747d85d62f417 100644 +index 360e72b8e540f3cdb557bc59bca5e0a9cda239f8..d424337f7ee70d6b488dcb8b59a1bd59b87e3e53 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -231,6 +231,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final it.unimi.dsi.fastutil.longs.Long2IntMap minimalTNT = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(); // Sakura - visibility api public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura - cannon entity merging - public final me.samsuik.sakura.explosion.DensityCache densityCache = new me.samsuik.sakura.explosion.DensityCache(); // Sakura - specialised density cache for swinging explosions + public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache + public final me.samsuik.sakura.explosion.durable.DurableBlockManager durabilityManager = new me.samsuik.sakura.explosion.durable.DurableBlockManager(); // Sakura - explosion durable blocks // Sakura start - add entity retrival methods with search limits diff --git a/patches/server/0032-Destroy-Waterlogged-Blocks.patch b/patches/server/0032-Destroy-Waterlogged-Blocks.patch new file mode 100644 index 0000000..0e5305b --- /dev/null +++ b/patches/server/0032-Destroy-Waterlogged-Blocks.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik <40902469+Samsuik@users.noreply.github.com> +Date: Thu, 16 Nov 2023 00:59:04 +0000 +Subject: [PATCH] Destroy Waterlogged Blocks + + +diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java +index 7ff26a8df45547b430ff470c32cea81697413bba..9f4fbb9475ab011ab6b19e709b6673cd5c9d0895 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -283,7 +283,11 @@ public class Explosion { + + if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { + return Optional.of(material.resistance()); ++ // Sakura start - destroy water logged blocks ++ } else if (!fluidState.isEmpty() && this.level.sakuraConfig().cannons.explosion.destroyWaterloggedBlocks) { ++ return Optional.of(ZERO_RESISTANCE); + } ++ // Sakura end - destroy water logged blocks + } + + return this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState); diff --git a/patches/server/0030-Cache-Vanillia-and-Eigen-Redstone.patch b/patches/server/0033-Cache-Vanillia-and-Eigen-Redstone.patch similarity index 97% rename from patches/server/0030-Cache-Vanillia-and-Eigen-Redstone.patch rename to patches/server/0033-Cache-Vanillia-and-Eigen-Redstone.patch index c8e5050..9061993 100644 --- a/patches/server/0030-Cache-Vanillia-and-Eigen-Redstone.patch +++ b/patches/server/0033-Cache-Vanillia-and-Eigen-Redstone.patch @@ -341,24 +341,24 @@ index 0000000000000000000000000000000000000000..c4150b062ee1a15f1938fea2da926699 + +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 8f5c2d979a6d902d142ba57a824beddf857f54b4..4cd97c07481ea5e9bcb5c69091f1e8d40255e218 100644 +index 622ab8f82f1b641f32912788ce44381b88f46093..4d16f579d7c73e2374230df519ca3f56c7c14dd5 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1751,6 +1751,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 1.0) continue; - - double x = entity.getX() - pos.x; -- double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - pos.y; -+ double y = entity.getEyeY() - pos.y; // Sakura - remove tnt special case - double z = entity.getZ() - pos.z; - double distance = Math.sqrt(x * x + y * y + z * z); - -+ // Sakura start -+ if (this.physics.before(1_17_0)) { -+ distanceFromBottom = (float) distanceFromBottom; -+ distance = (float) distance; -+ } -+ // Sakura end -+ - if (distance == 0.0D) continue; - - x /= distance; -@@ -308,10 +315,17 @@ public final class SakuraExplosion extends Explosion { +diff --git a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +index 929231896288c8355c17ba878d3dc296e9b258da..ca7819aa341529d8515e9db7ffe3b9d9186e05f3 100644 +--- a/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java ++++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java +@@ -169,9 +169,15 @@ public abstract class SpecialisedExplosion extends Explosion { if (distanceFromBottom <= 1.0) { double x = entity.getX() - pos.x; -- double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - pos.y; -+ double y = entity.getEyeY() - pos.y; // Sakura - remove tnt special case +- double y = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y; ++ double y = entity.getEyeY() - pos.y; // Sakura - physics version api double z = entity.getZ() - pos.z; double distance = Math.sqrt(x * x + y * y + z * z); - -+ // Sakura start ++ // Sakura start - physics version api + if (this.physics.before(1_17_0)) { -+ distanceFromBottom = (float) distanceFromBottom; ++ distanceFromBottom = (float) distanceFromBottom; + distance = (float) distance; + } -+ // Sakura end -+ ++ // Sakura end - physics version api + if (distance != 0.0D) { x /= distance; - y /= distance; +diff --git a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +index b94861d545cefb0e68a83efa6812a1ff4a381027..26e03034f037ff236b8be781a433fdfe69eb0764 100644 +--- a/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java ++++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java +@@ -36,6 +36,13 @@ public final class TntExplosion extends SpecialisedExplosion { + this.bounds = new AABB(x, y, z, x, y, z); + } + ++ // Sakura start - physics version api ++ @Override ++ protected double getExplosionOffset() { ++ return this.physics.before(1_10_0) ? (double) 0.49f : super.getExplosionOffset(); ++ } ++ // Sakura end - physics version api ++ + @Override + protected void startExplosion() { + for (int i = this.calculateExplosionPotential() - 1; i >= 0; --i) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 07c4cb5d91d0f7549fb92bfc03d730145e63a7d5..445899d4664b3bba467c0f8e5e390e13a2744b4d 100644 +index e3b6ef30a3c983e7e9b6bd83bc7baa600b16d850..c7a47485fd056159c448b63217afb8d4cacb1429 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -380,7 +380,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S @@ -267,7 +255,7 @@ index 07c4cb5d91d0f7549fb92bfc03d730145e63a7d5..445899d4664b3bba467c0f8e5e390e13 if (this.level().hasChunksAt(blockposition, blockposition1)) { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index 2c51b5f927fc08b5077bd0e58b08ce2acbb0331a..f22b2c52aaeab6757b4a50accb8fb6c66ffd1c1f 100644 +index 05c5b6be55bf6a81ff454532b40a372f9b9c8e13..b5ecadeb96750cbdca2023eefe7778596a040c97 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -90,6 +90,8 @@ public class FallingBlockEntity extends Entity { @@ -405,7 +393,7 @@ index 2c51b5f927fc08b5077bd0e58b08ce2acbb0331a..f22b2c52aaeab6757b4a50accb8fb6c6 } 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 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663e804d1c6 100644 +index 9493fbae91f0ef6c3aaf81e548c225748f6f83b5..8a7864943bf6b3baaa4a5ca5c2052411af3d23da 100644 --- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java +++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java @@ -60,6 +60,13 @@ public class PrimedTnt extends Entity implements TraceableEntity { @@ -422,7 +410,7 @@ index 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663 } @Override -@@ -130,12 +137,30 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -107,12 +114,30 @@ public class PrimedTnt extends Entity implements TraceableEntity { } } // Sakura end @@ -454,7 +442,7 @@ index 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663 } this.moveBasic(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise simple entity movement -@@ -145,15 +170,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -122,15 +147,19 @@ public class PrimedTnt extends Entity implements TraceableEntity { return; } // Paper end - Configurable TNT height nerf @@ -477,7 +465,7 @@ index 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663 // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event // this.discard(); this.respawnMerged(); // Sakura -@@ -206,7 +235,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -183,7 +212,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive)this.getBukkitEntity()); if (!event.isCancelled()) { @@ -489,7 +477,7 @@ index 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663 } // CraftBukkit end } -@@ -267,7 +299,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { +@@ -244,7 +276,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { // Paper start - Option to prevent TNT from moving in water @Override public boolean isPushedByFluid() { @@ -499,14 +487,14 @@ index 19397e2556a3cdc7180a5f8889aefb5ef23715b7..26fc6a8018cfde3c219a7d828f743663 // Paper end - Option to prevent TNT from moving in water } diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index b01930533e4c24d3fa4566a8462ac7ceeb9184f1..dfc30155d1b636ed8381a61fd61c74bc00b11b6d 100644 +index 71c2e458673372a8a332c49ab3cb0cf8560b970e..b52270ce23eb67d7b9acac53a4332191bb88f057 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -74,6 +74,7 @@ public class Explosion { public boolean wasCanceled = false; public float yield; // CraftBukkit end -+ protected final me.samsuik.sakura.physics.PhysicsVersion physics; // Sakura - physics version ++ protected final me.samsuik.sakura.physics.PhysicsVersion physics; // Sakura - physics version api public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) { return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source)); @@ -514,31 +502,25 @@ index b01930533e4c24d3fa4566a8462ac7ceeb9184f1..dfc30155d1b636ed8381a61fd61c74bc this.largeExplosionParticles = emitterParticle; this.explosionSound = soundEvent; this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit -+ this.physics = entity != null ? entity.physics() : world.localConfig().config(BlockPos.containing(x, y, z)).physicsVersion; // Sakura ++ this.physics = entity != null ? entity.physics() : world.localConfig().config(BlockPos.containing(x, y, z)).physicsVersion; // Sakura - physics version api } - // Paper start - optimise collisions -@@ -486,9 +488,17 @@ public class Explosion { - Vec3 vec3d1 = new Vec3(d8 + d3, d9, d10 + d4); - - // Sakura start -+ final net.minecraft.world.phys.HitResult.Type hitResult; - 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) { -+ hitResult = data.density() == 1.0 ? net.minecraft.world.phys.HitResult.Type.MISS : net.minecraft.world.phys.HitResult.Type.BLOCK; -+ } else { -+ if (entity.physics().before(1_14_0)) { -+ hitResult = entity.level().rayTrace(vec3d1, source); -+ } else { -+ hitResult = entity.level().clip(new ClipContext(vec3d1, source, entity.physics().afterOrEqual(1_16_0) ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity)).getType(); -+ } -+ } -+ if (hitResult == net.minecraft.world.phys.HitResult.Type.MISS) { - // Sakura end - ++i; + // Sakura start - optimise paper explosions +@@ -503,8 +505,12 @@ public class Explosion { + final float density = entity.level().densityCache.getKnownDensity(vec3d1); + if (density != me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { + hitResult = density != 0.0f ? net.minecraft.world.phys.HitResult.Type.MISS : net.minecraft.world.phys.HitResult.Type.BLOCK; ++ // Sakura start - physics version api ++ } else if (entity.physics().before(1_14_0)) { ++ hitResult = entity.level().rayTrace(vec3d1, source); + } else { +- hitResult = entity.level().clip(new ClipContext(vec3d1, source, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType(); ++ hitResult = entity.level().clip(new ClipContext(vec3d1, source, entity.physics().afterOrEqual(1_16_0) ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, entity)).getType(); ++ // Sakura end - physics version api } -@@ -602,6 +612,14 @@ public class Explosion { + if (hitResult == HitResult.Type.MISS) { + // Sakura end - replace density cache +@@ -609,6 +615,14 @@ public class Explosion { } if (cachedBlock.outOfWorld) { @@ -553,43 +535,52 @@ index b01930533e4c24d3fa4566a8462ac7ceeb9184f1..dfc30155d1b636ed8381a61fd61c74bc break; } -@@ -672,10 +690,17 @@ public class Explosion { +@@ -714,9 +728,15 @@ public class Explosion { if (d7 <= 1.0D) { double d8 = entity.getX() - this.x; - double d9 = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y; -+ double d9 = entity.getEyeY() - this.y; // Sakura - remove tnt special case ++ double d9 = entity.getEyeY() - this.y; // Sakura - physics version api double d10 = entity.getZ() - this.z; double d11 = Math.sqrt(d8 * d8 + d9 * d9 + d10 * d10); - -+ // Sakura start ++ // Sakura start - physics version api + if (this.physics.before(1_17_0)) { + d7 = (float) d7; + d11 = (float) d11; + } -+ // Sakura end -+ ++ // Sakura end - physics version api + if (d11 != 0.0D) { d8 /= d11; - d9 /= d11; -@@ -1003,7 +1028,14 @@ public class Explosion { - return data.density(); +@@ -1048,7 +1068,7 @@ public class Explosion { + // Sakura start - replace density cache + float blockDensity = this.level.densityCache.getDensity(vec3d, entity); + if (blockDensity == me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) { +- blockDensity = this.getSeenFraction(vec3d, entity, blockCache, blockPos); // Paper - optimise explosions; ++ blockDensity = this.getSeenPercent(vec3d, entity, blockCache, blockPos); // Sakura - physics version api // Paper - optimise explosions; + this.level.densityCache.putDensity(vec3d, entity, blockDensity); + // Sakura end - replace density cache } +@@ -1056,6 +1076,17 @@ public class Explosion { + return blockDensity; + } -- float blockDensity = this.getSeenFraction(vec3d, entity, data, blockCache, blockPos); // Paper - optimise explosions; -+ // Sakura start - physics version api -+ final float blockDensity; ++ // Sakura start - physics version api ++ private float getSeenPercent(Vec3 vec3d, Entity entity, ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { + if (this.physics.afterOrEqual(1_16_0)) { -+ blockDensity = this.getSeenFraction(vec3d, entity, data, blockCache, blockPos); // Paper - optimise explosions; ++ // Papers impl is untouched, intended to be used as a fast path. ++ return this.getSeenFraction(vec3d, entity, blockCache, blockPos); + } else { -+ blockDensity = getSeenPercent(vec3d, entity, data); ++ return getSeenPercent(vec3d, entity); + } -+ // Sakura end - - if (data == null || !data.isExpandable() && (blockDensity == 0.0f || blockDensity == 1.0f)) { - level.densityCache.createCache(key, entity, vec3d, blockDensity); ++ } ++ // Sakura end - physics version api ++ + static class CacheKey { + private final Level world; + private final double posX, posY, posZ; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index d54fb6906d66a6feeea3463cb2f38d10b52d2e41..19b50f4f68c3a389f1169cf072dfd0a896475fe3 100644 +index 8e5bd44b93cfd4b3abdb76a5ee6dd6eb4e714223..ea15ef94540b78029c8f78021ace6998f62a701a 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -266,6 +266,205 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0042-Allow-explosions-to-destroy-lava.patch b/patches/server/0042-Allow-explosions-to-destroy-lava.patch deleted file mode 100644 index 08cd736..0000000 --- a/patches/server/0042-Allow-explosions-to-destroy-lava.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samsuik <40902469+Samsuik@users.noreply.github.com> -Date: Sat, 25 Nov 2023 21:14:45 +0000 -Subject: [PATCH] Allow explosions to destroy lava - - -diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 50621055665ca973875fa06e5fd274c6398fa06f..03466de75efd0c520b2d9ae2fbaeec46139b2633 100644 ---- a/src/main/java/net/minecraft/world/level/Explosion.java -+++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -228,6 +228,10 @@ public class Explosion { - if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { - resistance = Optional.of(material.resistance()); - } -+ -+ if (this.level.sakuraConfig().cannons.explosion.explodeLava && blockState.is(Blocks.LAVA)) { -+ resistance = Optional.of(0.0f); // 1.0 might be better here, 0.0 seems too much. -+ } - } - // Sakura end - explosion durable blocks - -@@ -440,6 +444,12 @@ public class Explosion { - return false; - } - -+ // Sakura start - explode lava -+ if (level.sakuraConfig().cannons.explosion.explodeLava && state.is(Blocks.LAVA)) { -+ return true; -+ } -+ // Sakura end -+ - float power = radius * 1.3f; - float blockRes = state.getBlock().getExplosionResistance(); - float fluidRes = state.getFluidState().getExplosionResistance(); diff --git a/patches/server/0039-Allow-water-in-the-nether.patch b/patches/server/0042-Allow-water-in-the-nether.patch similarity index 100% rename from patches/server/0039-Allow-water-in-the-nether.patch rename to patches/server/0042-Allow-water-in-the-nether.patch diff --git a/patches/server/0040-Configure-concrete-solidifying-in-water.patch b/patches/server/0043-Configure-concrete-solidifying-in-water.patch similarity index 92% rename from patches/server/0040-Configure-concrete-solidifying-in-water.patch rename to patches/server/0043-Configure-concrete-solidifying-in-water.patch index 3b14368..6b1df97 100644 --- a/patches/server/0040-Configure-concrete-solidifying-in-water.patch +++ b/patches/server/0043-Configure-concrete-solidifying-in-water.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configure concrete solidifying in water diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index f22b2c52aaeab6757b4a50accb8fb6c66ffd1c1f..bf6281dabd78fdfaf9dc1a036c9a2193a03fdb5c 100644 +index b5ecadeb96750cbdca2023eefe7778596a040c97..fd043df496bded2fb18dae0b8053cd45c5d8c500 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -277,7 +277,7 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0041-Option-for-fast-nether-dimension-lava.patch b/patches/server/0044-Option-for-fast-nether-dimension-lava.patch similarity index 100% rename from patches/server/0041-Option-for-fast-nether-dimension-lava.patch rename to patches/server/0044-Option-for-fast-nether-dimension-lava.patch diff --git a/patches/server/0045-Allow-explosions-to-destroy-lava.patch b/patches/server/0045-Allow-explosions-to-destroy-lava.patch new file mode 100644 index 0000000..6ac2a49 --- /dev/null +++ b/patches/server/0045-Allow-explosions-to-destroy-lava.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik <40902469+Samsuik@users.noreply.github.com> +Date: Sat, 25 Nov 2023 21:14:45 +0000 +Subject: [PATCH] Allow explosions to destroy lava + + +diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java +index 9da068ebf5c0245aa099af9e53d34f1dfb5cdde9..3b9e5ffe45dcf1e4a0b2c5d334b0bec666594fa6 100644 +--- a/src/main/java/net/minecraft/world/level/Explosion.java ++++ b/src/main/java/net/minecraft/world/level/Explosion.java +@@ -286,7 +286,7 @@ public class Explosion { + if (material != null && material.resistance() >= 0.0f && (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt)) { + return Optional.of(material.resistance()); + // Sakura start - destroy water logged blocks +- } else if (!fluidState.isEmpty() && this.level.sakuraConfig().cannons.explosion.destroyWaterloggedBlocks) { ++ } else if (!fluidState.isEmpty() && this.level.sakuraConfig().cannons.explosion.destroyWaterloggedBlocks || blockState.is(Blocks.LAVA) && this.level.sakuraConfig().cannons.explosion.explodeLava) { // Sakura - allow explosions to destroy lava + return Optional.of(ZERO_RESISTANCE); + } + // Sakura end - destroy water logged blocks diff --git a/patches/server/0043-Disable-bubble-columns-affecting-cannon-entities.patch b/patches/server/0046-Disable-bubble-columns-affecting-cannon-entities.patch similarity index 100% rename from patches/server/0043-Disable-bubble-columns-affecting-cannon-entities.patch rename to patches/server/0046-Disable-bubble-columns-affecting-cannon-entities.patch diff --git a/patches/server/0044-Treat-all-collidable-blocks-as-full-while-moving-fas.patch b/patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch similarity index 98% rename from patches/server/0044-Treat-all-collidable-blocks-as-full-while-moving-fas.patch rename to patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch index 33003d8..ad59e56 100644 --- a/patches/server/0044-Treat-all-collidable-blocks-as-full-while-moving-fas.patch +++ b/patches/server/0047-Treat-all-collidable-blocks-as-full-while-moving-fas.patch @@ -59,7 +59,7 @@ index 510d722fffd4bdcee2db42aefa662c49563ffa81..37b6d16f0b1e000eb4082a530c7f7134 AABB singleAABB = blockCollision.getSingleAABBRepresentation(); if (singleAABB != null) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 445899d4664b3bba467c0f8e5e390e13a2744b4d..9f1e0fd02ff2fcddf0d3e768167350fd4068da05 100644 +index c7a47485fd056159c448b63217afb8d4cacb1429..0ebe9db8b648f49819574ae8e495866d33394a57 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -581,6 +581,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S diff --git a/patches/server/0045-Add-redstone-implementation-API.patch b/patches/server/0048-Add-redstone-implementation-API.patch similarity index 100% rename from patches/server/0045-Add-redstone-implementation-API.patch rename to patches/server/0048-Add-redstone-implementation-API.patch diff --git a/patches/server/0046-Reduce-entity-tracker-player-updates.patch b/patches/server/0049-Reduce-entity-tracker-player-updates.patch similarity index 100% rename from patches/server/0046-Reduce-entity-tracker-player-updates.patch rename to patches/server/0049-Reduce-entity-tracker-player-updates.patch diff --git a/patches/server/0047-Add-option-for-legacy-lava-block-formation.patch b/patches/server/0050-Add-option-for-legacy-lava-block-formation.patch similarity index 100% rename from patches/server/0047-Add-option-for-legacy-lava-block-formation.patch rename to patches/server/0050-Add-option-for-legacy-lava-block-formation.patch diff --git a/patches/server/0048-Configure-mob-spawner-defaults.patch b/patches/server/0051-Configure-mob-spawner-defaults.patch similarity index 100% rename from patches/server/0048-Configure-mob-spawner-defaults.patch rename to patches/server/0051-Configure-mob-spawner-defaults.patch diff --git a/patches/server/0049-Allow-disabling-random-dispenser-item-selection.patch b/patches/server/0052-Allow-disabling-random-dispenser-item-selection.patch similarity index 100% rename from patches/server/0049-Allow-disabling-random-dispenser-item-selection.patch rename to patches/server/0052-Allow-disabling-random-dispenser-item-selection.patch diff --git a/patches/server/0050-Add-instant-mob-death-animation.patch b/patches/server/0053-Add-instant-mob-death-animation.patch similarity index 100% rename from patches/server/0050-Add-instant-mob-death-animation.patch rename to patches/server/0053-Add-instant-mob-death-animation.patch diff --git a/patches/server/0051-Configure-fluids-breaking-redstone.patch b/patches/server/0054-Configure-fluids-breaking-redstone.patch similarity index 100% rename from patches/server/0051-Configure-fluids-breaking-redstone.patch rename to patches/server/0054-Configure-fluids-breaking-redstone.patch diff --git a/patches/server/0052-Option-to-disable-explosions-hurting-players.patch b/patches/server/0055-Option-to-disable-explosions-hurting-players.patch similarity index 100% rename from patches/server/0052-Option-to-disable-explosions-hurting-players.patch rename to patches/server/0055-Option-to-disable-explosions-hurting-players.patch diff --git a/patches/server/0053-Iron-golems-take-fall-damage.patch b/patches/server/0056-Iron-golems-take-fall-damage.patch similarity index 100% rename from patches/server/0053-Iron-golems-take-fall-damage.patch rename to patches/server/0056-Iron-golems-take-fall-damage.patch diff --git a/patches/server/0054-Add-explosions-dropping-items-config.patch b/patches/server/0057-Add-explosions-dropping-items-config.patch similarity index 64% rename from patches/server/0054-Add-explosions-dropping-items-config.patch rename to patches/server/0057-Add-explosions-dropping-items-config.patch index 00267a6..a1b703c 100644 --- a/patches/server/0054-Add-explosions-dropping-items-config.patch +++ b/patches/server/0057-Add-explosions-dropping-items-config.patch @@ -5,19 +5,18 @@ Subject: [PATCH] Add explosions dropping items config diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java -index 06db07190dcbe7d6e9dee7a370e9b471ee33b514..dc14bfe889205f4f9002ffdbcee6993a2ac6c83a 100644 +index 3b9e5ffe45dcf1e4a0b2c5d334b0bec666594fa6..88a7d47c9c7d142474f54342331a31ade829c9ed 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java -@@ -906,6 +906,12 @@ public class Explosion { - }); +@@ -941,6 +941,11 @@ public class Explosion { + this.level.densityCache.clear(-1); } - -+ // Sakura start + // Sakura end - explosion density cache ++ // Sakura start - config for explosions dropping items + if (!level.sakuraConfig().cannons.explosion.explosionsDropItems) { + list.clear(); + } -+ // Sakura end -+ ++ // Sakura end - config for explosions dropping items Iterator iterator = list.iterator(); while (iterator.hasNext()) { diff --git a/patches/server/0055-Optimise-check-inside-blocks-and-fluids.patch b/patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch similarity index 98% rename from patches/server/0055-Optimise-check-inside-blocks-and-fluids.patch rename to patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch index 42d5789..243fe9d 100644 --- a/patches/server/0055-Optimise-check-inside-blocks-and-fluids.patch +++ b/patches/server/0058-Optimise-check-inside-blocks-and-fluids.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Optimise check inside blocks and fluids diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 9f1e0fd02ff2fcddf0d3e768167350fd4068da05..a7d57d6549e6e233dcac6f3fd61a1aee83b8acd4 100644 +index 0ebe9db8b648f49819574ae8e495866d33394a57..bb213891e23b9e6e04d34dda94178db3235b6b7b 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -1997,18 +1997,37 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S diff --git a/patches/server/0056-Fix-paper-findSupportingBlock-not-updating-last-chun.patch b/patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch similarity index 90% rename from patches/server/0056-Fix-paper-findSupportingBlock-not-updating-last-chun.patch rename to patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch index 6ea0176..a15c17a 100644 --- a/patches/server/0056-Fix-paper-findSupportingBlock-not-updating-last-chun.patch +++ b/patches/server/0059-Fix-paper-findSupportingBlock-not-updating-last-chun.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix paper findSupportingBlock not updating last chunk diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 19b50f4f68c3a389f1169cf072dfd0a896475fe3..c824dc2477b309d571231d3e80deb838d28bd0e7 100644 +index ea15ef94540b78029c8f78021ace6998f62a701a..0572cf39080e549354b5adf437afc7dc3e8e824c 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -896,6 +896,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch b/patches/server/0060-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch similarity index 100% rename from patches/server/0057-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch rename to patches/server/0060-Avoid-searching-for-lava-if-throttled-water-flow-spe.patch diff --git a/patches/server/0058-Calculate-biome-noise-once-per-chunk-section.patch b/patches/server/0061-Calculate-biome-noise-once-per-chunk-section.patch similarity index 100% rename from patches/server/0058-Calculate-biome-noise-once-per-chunk-section.patch rename to patches/server/0061-Calculate-biome-noise-once-per-chunk-section.patch diff --git a/patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch b/patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch similarity index 95% rename from patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch rename to patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch index 3d31dbf..61e972e 100644 --- a/patches/server/0059-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch +++ b/patches/server/0062-Fix-doEntityDrops-gamerule-preventing-falling-blocks.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix doEntityDrops gamerule preventing falling blocks from diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java -index bf6281dabd78fdfaf9dc1a036c9a2193a03fdb5c..f2fbe8f7015a6614e0fec4f59e16cfbecb0cbc55 100644 +index fd043df496bded2fb18dae0b8053cd45c5d8c500..b773a8b831f1d65f6d442e95b1ab685170567a9c 100644 --- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -352,10 +352,14 @@ public class FallingBlockEntity extends Entity { diff --git a/patches/server/0060-Add-entity-travel-distance-limits.patch b/patches/server/0063-Add-entity-travel-distance-limits.patch similarity index 93% rename from patches/server/0060-Add-entity-travel-distance-limits.patch rename to patches/server/0063-Add-entity-travel-distance-limits.patch index c71ffb9..76d021b 100644 --- a/patches/server/0060-Add-entity-travel-distance-limits.patch +++ b/patches/server/0063-Add-entity-travel-distance-limits.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add entity travel distance limits diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 17c40c04f1a1df05b9740c9e32078d0347c4baa4..d6474f27806b2c7a6f7e844cfaaea4985dddd6c0 100644 +index ec7d7f22b267ae6572e6005f10221755cbb1a480..2a9a6a9f00343f614a0d2430095a17088861eb1f 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -1417,6 +1417,11 @@ public class ServerLevel extends Level implements WorldGenLevel { @@ -21,7 +21,7 @@ index 17c40c04f1a1df05b9740c9e32078d0347c4baa4..d6474f27806b2c7a6f7e844cfaaea498 } else { entity.inactiveTick(); } // Paper - EAR 2 this.getProfiler().pop(); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index a7d57d6549e6e233dcac6f3fd61a1aee83b8acd4..bdae4d33d01569accd6a184b5bed2539a68ec457 100644 +index bb213891e23b9e6e04d34dda94178db3235b6b7b..9c7bea04facfb2c2de68c1861e03c0b2a22228ce 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -730,6 +730,19 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S diff --git a/patches/server/0061-Configure-potion-speed-and-breaking-inside-entities.patch b/patches/server/0064-Configure-potion-speed-and-breaking-inside-entities.patch similarity index 100% rename from patches/server/0061-Configure-potion-speed-and-breaking-inside-entities.patch rename to patches/server/0064-Configure-potion-speed-and-breaking-inside-entities.patch diff --git a/patches/server/0062-Add-outline-colliison-to-enderpearls.patch b/patches/server/0065-Add-outline-colliison-to-enderpearls.patch similarity index 100% rename from patches/server/0062-Add-outline-colliison-to-enderpearls.patch rename to patches/server/0065-Add-outline-colliison-to-enderpearls.patch diff --git a/patches/server/0063-Disable-player-poses-shrinking-collision-box.patch b/patches/server/0066-Disable-player-poses-shrinking-collision-box.patch similarity index 100% rename from patches/server/0063-Disable-player-poses-shrinking-collision-box.patch rename to patches/server/0066-Disable-player-poses-shrinking-collision-box.patch diff --git a/patches/server/0064-Mob-spawner-behaviour.patch b/patches/server/0067-Mob-spawner-behaviour.patch similarity index 100% rename from patches/server/0064-Mob-spawner-behaviour.patch rename to patches/server/0067-Mob-spawner-behaviour.patch