mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-22 00:09:20 +00:00
The player velocity was not being talied correctly, and velocity was not sent when explosion effects were disabled in fps settings.
623 lines
28 KiB
Diff
623 lines
28 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Samsuik <kfian294ma4@gmail.com>
|
|
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<E> {
|
|
}
|
|
}
|
|
|
|
+ // 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..ce676a021dd375959e9403b237bad864cd2b7da2
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java
|
|
@@ -0,0 +1,201 @@
|
|
+package me.samsuik.sakura.explosion.special;
|
|
+
|
|
+import io.papermc.paper.util.WorldUtil;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Holder;
|
|
+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<T extends Entity> 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, Holder<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.getHitPlayers().clear();
|
|
+ }
|
|
+
|
|
+ this.getToBlow().clear();
|
|
+ return destroyedBlocks;
|
|
+ }
|
|
+
|
|
+ protected void postExplosion(List<BlockPos> foundBlocks, boolean destroyedBlocks) {
|
|
+ // optimisation: Keep the block cache across explosions and invalidate any found blocks.
|
|
+ // This can help reduce block retrievals while block searching when there's a durable block,
|
|
+ // and when ray tracing for obstructions. This is disabled by default because plugins can
|
|
+ // change blocks in the explosion event.
|
|
+ if (this.level.sakuraConfig().cannons.explosion.useBlockCacheAcrossExplosions && !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<Entity[]> 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;
|
|
+
|
|
+ if (exposure == 0.0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ x *= exposure;
|
|
+ y *= exposure;
|
|
+ z *= exposure;
|
|
+
|
|
+ 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..6bcef992766b901db34494a4d359ba335705b689
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java
|
|
@@ -0,0 +1,201 @@
|
|
+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.Holder;
|
|
+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.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.bukkit.util.Vector;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.util.List;
|
|
+
|
|
+public final class TntExplosion extends SpecialisedExplosion<PrimedTnt> {
|
|
+ private static final int ALL_DIRECTIONS = 0b111;
|
|
+ private static final int FOUND_ALL_BLOCKS = ALL_DIRECTIONS + 12;
|
|
+
|
|
+ private final Vec3 originalPosition;
|
|
+ private final List<Vec3> explosions = new ObjectArrayList<>();
|
|
+ private AABB bounds;
|
|
+ private int wrapped = 0;
|
|
+ private boolean moved = false;
|
|
+
|
|
+ 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, Holder<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<BlockPos> 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<BlockPos> 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 Vector getCauseOrigin() {
|
|
+ Vector origin = this.cause.getOriginVector();
|
|
+ return origin == null ? new Vector(this.x, this.y, this.z) : origin;
|
|
+ }
|
|
+
|
|
+ private EntityState nextSourceVelocity() {
|
|
+ Vector origin = this.getCauseOrigin(); // valid position to use while creating a temporary entity
|
|
+ PrimedTnt tnt = new PrimedTnt(this.level, origin.getX(), origin.getY(), origin.getZ(), null);
|
|
+ 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) {
|
|
+ // Before setting entity state, otherwise we might cause issues.
|
|
+ final boolean hasMoved;
|
|
+ if (this.moved) {
|
|
+ hasMoved = true;
|
|
+ } else if (this.position.equals(this.cause.position())) {
|
|
+ hasMoved = false;
|
|
+ } else {
|
|
+ double newMomentum = entityState.momentum().lengthSqr();
|
|
+ double oldMomentum = this.cause.entityState().momentum().lengthSqr();
|
|
+ hasMoved = oldMomentum <= Math.pow(this.radius() * 2.0 + 1.0, 2.0) || newMomentum <= oldMomentum;
|
|
+ }
|
|
+
|
|
+ // Keep track of entity state
|
|
+ entityState.apply(this.cause);
|
|
+ this.cause.storeEntityState();
|
|
+
|
|
+ // Ticking is always required after destroying a block.
|
|
+ if (destroyedBlocks || hasMoved) {
|
|
+ this.cause.setFuse(100);
|
|
+ this.cause.tick();
|
|
+ this.moved |= !this.position.equals(this.originalPosition);
|
|
+ this.recalculateExplosionPosition();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private int calculateExplosionPotential() {
|
|
+ IteratorSafeOrderedReferenceSet<Entity> 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.isRemoved() || !foundEntity.compareState(this.cause) || !foundEntity.isSafeToMergeInto(this.cause))
|
|
+ break;
|
|
+ base += foundEntity.getStacked();
|
|
+ foundEntity.discard();
|
|
+ foundEntity.updateEntityHandle(this.cause);
|
|
+ }
|
|
+
|
|
+ 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 352a8d62dd25c4c612abe016c7038bdfd13006f2..e68ea2ac656085fbe25eb19c14793889cdafab8c 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1964,6 +1964,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()) {
|
|
@@ -1986,7 +1992,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 dc9d72bc033735dd83d7b0f3fe79d0a51b9661ab..6cfa54da058939003576025cb0512e0bcc3e715b 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
@@ -84,28 +84,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
|
&& (tnt.entityState().fallDistance() == fallDistance
|
|
|| 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 103eca429d899f4d004ac0cb1996339e66543080..b2ba5d761e21023ef4e3ee01c7318a7a586e650b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Explosion.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
@@ -56,9 +56,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;
|
|
@@ -186,7 +188,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<ExplosionBlockCache> blockCache = null;
|
|
+ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<ExplosionBlockCache> blockCache = null; // Sakura - private -> protected
|
|
|
|
public static final class ExplosionBlockCache {
|
|
|
|
@@ -213,7 +215,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<BlockPos> 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) {
|
|
@@ -311,7 +335,7 @@ public class Explosion {
|
|
(currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
|
ExplosionBlockCache cachedBlock = blockCache[cacheKey];
|
|
if (cachedBlock == null || cachedBlock.key != key) {
|
|
- blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, false);
|
|
+ blockCache[cacheKey] = cachedBlock = this.getOrCacheExplosionBlock(currX, currY, currZ, key, this.level.sakuraConfig().cannons.explosion.useBlockCacheAcrossExplosions); // Sakura - specialised explosions
|
|
}
|
|
|
|
final BlockState blockState = cachedBlock.blockState;
|
|
@@ -502,14 +526,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;
|
|
{
|
|
@@ -757,7 +774,10 @@ public class Explosion {
|
|
Player entityhuman = (Player) entity;
|
|
|
|
if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback
|
|
- this.hitPlayers.put(entityhuman, vec3d1);
|
|
+ // Sakura start - specialised explosions; tally player velocity
|
|
+ final Vec3 explosionImpact = vec3d1;
|
|
+ this.hitPlayers.compute(entityhuman, (p, v) -> v != null ? v.add(explosionImpact) : explosionImpact);
|
|
+ // Sakura end - specialised explosions; tally player velocity
|
|
}
|
|
}
|
|
|
|
@@ -994,7 +1014,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 d41086a80ea03855fde694947fa61f2d48eaa114..507d676c58daf2297eb729543a7f86c896a984d6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -1407,7 +1407,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);
|