Skip to content

Commit

Permalink
Clean up and fix numerous issues with optimise explosions
Browse files Browse the repository at this point in the history
  • Loading branch information
Samsuik committed Dec 14, 2023
1 parent 8061aef commit 63cc76f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 57 deletions.
120 changes: 66 additions & 54 deletions patches/server/0020-Optimised-Explosions.patch
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,21 @@ index 0000000000000000000000000000000000000000..3f6f34cc617efaad420485a7f613cfca
+}
diff --git a/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f9880d35347dd008aa2ee6e67f35301ff37a4c0
index 0000000000000000000000000000000000000000..8a8b4169c8928459f6b51150bd8e67d0a309ca08
--- /dev/null
+++ b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
@@ -0,0 +1,391 @@
@@ -0,0 +1,403 @@
+package me.samsuik.sakura.explosion;
+
+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;
Expand Down Expand Up @@ -201,40 +203,39 @@ index 0000000000000000000000000000000000000000..4f9880d35347dd008aa2ee6e67f35301
+ @Override
+ public void explode() {
+ if (this.radius < 0.1F) {
+ for (int i = 1; i < source.getStacked() && !wasCanceled; ++i) {
+ this.finalizeExplosion(false);
+ for (int i = 1; i < source.getStacked(); ++i) {
+ getToBlow().clear();
+ ((ServerLevel) level).notifyPlayersOfExplosion(x, y, z, radius, this);
+ finalizeExplosion(false);
+ }
+
+ return;
+ }
+
+ PrimedTnt origin = (PrimedTnt) source;
+ List<Vec3> positions = new ArrayList<>(origin.getStacked());
+
+ // This is a temporary entity that will be used for movement.
+ PrimedTnt tnt = new PrimedTnt(level, 0, 0, 0, null);
+ AABB bounds = new AABB(x, y, z, x, y, z);
+
+ origin.entityState().apply(tnt);
+
+ Vec3 lastMovement = tnt.getDeltaMovement();
+ List<Vec3> 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 < origin.getStacked() && !wasCanceled; ++i) {
+ for (int i = 0; i < source.getStacked() && !wasCanceled; ++i) {
+ if (i > 0) {
+ updatePosition(origin, tnt);
+ calculateNextPosition(entityState);
+ getToBlow().clear();
+ }
+
+ // block at explosion position
+ int blockX = Mth.floor(x);
+ int blockY = Mth.floor(y);
+ int blockZ = Mth.floor(z);
+ // 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) {
+ getToBlow().clear();
+ 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);
Expand All @@ -244,70 +245,81 @@ index 0000000000000000000000000000000000000000..4f9880d35347dd008aa2ee6e67f35301
+ }
+ }
+
+ // keep track of positions and bounds
+ positions.add(position);
+ bounds = bounds.minmax(new AABB(position, position));
+
+ Vec3 movement = tnt.getDeltaMovement();
+
+ // Check if the explosion has wrapped around with swinging on each axis
+ if (wrapped < 7) {
+ // Check if the explosion has wrapped around with swinging on each axis
+ 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;
+ } else if (getToBlow().isEmpty() && level.sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) {
+ wrapped++;
+ } else {
+ wrapped = 7;
+ lastMovement = movement;
+ }
+
+ lastMovement = tnt.getDeltaMovement();
+ 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 (i + 1 < origin.getStacked()) {
+ if (!isFinalExplosion) {
+ BlockPos.MutableBlockPos mbp = new BlockPos.MutableBlockPos();
+ impactEntityIdle(tnt, new Entity[0], position, 1, radius * 2.0f, mbp, blockCache);
+
+ // Calculate next source velocity
+ entityState = calculateNextVelocity(position, mbp, blockCache);
+
+ // The purpose of this is to make sure papers blockCache doesn't become
+ // outdated by flushing the map and removing stale entries from the recent
+ // cache array. If there is any case where tnt can provide its self delta
+ // movement and then start moving without blocks this may break stuff to
+ // fix it see the note above or add a boolean to mark the cache as dirty
+ // outside this loop and then invalidate before the final impact entities.
+ if (!getToBlow().isEmpty() && tnt.getDeltaMovement().lengthSqr() <= 64.0) {
+ if (!getToBlow().isEmpty()) {
+ invalidateBlockCache(blockCache);
+ }
+
+ // could it be viable to have a configuration option to only
+ // call finalize explosion when blocks are found
+ // may affect plugins that need exact explosion positions
+ // Could be viable in the future to have a configuration option to reduce explosion events
+ super.finalizeExplosion(false);
+ ((ServerLevel) level).notifyPlayersOfExplosion(x, y, z, radius, this);
+ } else {
+ locateAndImpactEntities(positions, bounds, blockCache);
+
+ // Update wrapped, this is for tracking swinging and if blocks are found
+ if (getToBlow().isEmpty() && level.sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) {
+ wrapped++;
+ } else {
+ wrapped = 7;
+ }
+ }
+ }
+
+ clearBlockCache();
+ }
+
+ private void updatePosition(PrimedTnt origin, PrimedTnt tnt) {
+ boolean originMoved = !origin.position().equals(tnt.position());
+ private void calculateNextPosition(EntityState entityState) {
+ if (source instanceof PrimedTnt tnt) {
+ tnt.setFuse(100);
+ }
+
+ origin.setFuse(100);
+ tnt.storeEntityState();
+ tnt.entityState().apply(origin);
+ boolean moved = !source.entityState().position().equals(source.position());
+ entityState.apply(source);
+ source.storeEntityState();
+
+ // We have to check delta movement otherwise this optimisation can break reversing tnt.
+ // If origin was shot upwards to a block then falls in the explosion tick it will swing
+ // and origin and tnt will be in the same position every other tnt while swinging.
+ if (!getToBlow().isEmpty() || tnt.getDeltaMovement().lengthSqr() <= 64.0 || originMoved) {
+ origin.tick();
+ if (!getToBlow().isEmpty() || source.getDeltaMovement().lengthSqr() <= 65.16525625 || moved) {
+ source.tick();
+ }
+
+ // update explosion position
+ x = origin.getX();
+ y = origin.getY(0.0625);
+ z = origin.getZ();
+ x = source.getX();
+ y = source.getY();
+ 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<Vec3> positions, AABB bb, ExplosionBlockCache[] blockCache) {
Expand Down
15 changes: 12 additions & 3 deletions patches/server/0040-Configure-cannon-physics-by-version.patch
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@ index ae2eede559bd9fe7e500ce180f2ac102a95d3856..7ced5ae768cbea9ee0a7bab2365fbaef
if (xSmaller && z != 0.0) {
z = performAABBCollisionsZ(axisalignedbb, z, aabbs);
diff --git a/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
index 4f9880d35347dd008aa2ee6e67f35301ff37a4c0..79d5cd8f90fda2390a64c2d1e0cfca6b66ad83ac 100644
index 8a8b4169c8928459f6b51150bd8e67d0a309ca08..a291516ec7bdb9d8b840f41ca52e6bbaf8e2e08a 100644
--- a/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
+++ b/src/main/java/me/samsuik/sakura/explosion/SakuraExplosion.java
@@ -244,10 +244,17 @@ public class SakuraExplosion extends Explosion {
@@ -163,7 +163,7 @@ public class SakuraExplosion extends Explosion {

// update explosion position
x = source.getX();
- y = source.getY();
+ y = physics.before(1_10_0) ? source.getY() + (double) 0.49f : source.getY(0.0625D);
z = source.getZ();
}

@@ -256,10 +256,17 @@ public class SakuraExplosion extends Explosion {
if (distanceFromBottom > 1.0) continue;

double x = entity.getX() - pos.x;
Expand All @@ -58,7 +67,7 @@ index 4f9880d35347dd008aa2ee6e67f35301ff37a4c0..79d5cd8f90fda2390a64c2d1e0cfca6b
if (distance == 0.0D) continue;

x /= distance;
@@ -292,10 +299,17 @@ public class SakuraExplosion extends Explosion {
@@ -304,10 +311,17 @@ public class SakuraExplosion extends Explosion {

if (distanceFromBottom <= 1.0) {
double x = entity.getX() - pos.x;
Expand Down

0 comments on commit 63cc76f

Please sign in to comment.