mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-30 04:09:09 +00:00
The player velocity was not being talied correctly, and velocity was not sent when explosion effects were disabled in fps settings.
647 lines
31 KiB
Diff
647 lines
31 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/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
|
index c21e00812f1aaa1279834a0562d360d6b89e146c..1e1329adde1457898a3002279b53b1bbb91c36d2 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IteratorSafeOrderedReferenceSet.java
|
|
@@ -107,6 +107,12 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
|
}
|
|
}
|
|
|
|
+ // Sakura start - specialised explosions; add indexOf method
|
|
+ public int indexOf(final E element) {
|
|
+ return this.indexMap.getInt(element);
|
|
+ }
|
|
+ // Sakura end - specialised explosions; 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..99388d6e1d536f195d7602341313e73d6797d9db
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/special/SpecialisedExplosion.java
|
|
@@ -0,0 +1,223 @@
|
|
+package me.samsuik.sakura.explosion.special;
|
|
+
|
|
+import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache;
|
|
+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 int getExplosionCount();
|
|
+
|
|
+ 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.getExplosionCount(); ++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;
|
|
+
|
|
+ EntityLookup entityLookup = this.level.moonrise$getEntityLookup();
|
|
+ for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
|
|
+ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
|
|
+ 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 != this.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 (this.excludeSourceFromDamage && entity == this.source) {
|
|
+ return; // for paper api
|
|
+ }
|
|
+ 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()) - 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, 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;
|
|
+
|
|
+ this.applyEntityVelocity(entity, x, y, z, potential);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ protected final void applyEntityVelocity(Entity entity, double x, double y, double z, int potential) {
|
|
+ Vec3 movement = entity.getDeltaMovement();
|
|
+
|
|
+ double moveX = movement.x();
|
|
+ double moveY = movement.y();
|
|
+ double moveZ = movement.z();
|
|
+
|
|
+ for (int i = 0; i < potential; ++i) {
|
|
+ moveX += x;
|
|
+ moveY += y;
|
|
+ moveZ += z;
|
|
+ }
|
|
+
|
|
+ entity.setDeltaMovement(moveX, moveY, moveZ);
|
|
+ }
|
|
+}
|
|
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..a5d13c6b0d8765e4a65b43f5835fd907738e1da4
|
|
--- /dev/null
|
|
+++ b/src/main/java/me/samsuik/sakura/explosion/special/TntExplosion.java
|
|
@@ -0,0 +1,207 @@
|
|
+package me.samsuik.sakura.explosion.special;
|
|
+
|
|
+import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import me.samsuik.sakura.entity.EntityState;
|
|
+import me.samsuik.sakura.entity.merge.MergeLevel;
|
|
+import me.samsuik.sakura.entity.merge.MergeableEntity;
|
|
+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 int getExplosionCount() {
|
|
+ if (this.cause.getMergeEntityData().getMergeLevel() == MergeLevel.NONE) {
|
|
+ this.mergeEntitiesBeforeExplosion();
|
|
+ }
|
|
+ return this.cause.getMergeEntityData().getCount();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ protected void startExplosion() {
|
|
+ for (int i = this.getExplosionCount() - 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) {
|
|
+ int blockX = Mth.floor(this.x);
|
|
+ int blockY = Mth.floor(this.y);
|
|
+ int blockZ = Mth.floor(this.z);
|
|
+ long key = BlockPos.asLong(blockX, blockY, blockZ);
|
|
+ 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 void mergeEntitiesBeforeExplosion() {
|
|
+ IteratorSafeOrderedReferenceSet<Entity> entities = this.level.entityTickList.entities;
|
|
+ int index = entities.indexOf(this.cause);
|
|
+
|
|
+ entities.createRawIterator();
|
|
+ // 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 instanceof MergeableEntity mergeEntity) || foundEntity.isRemoved() || !foundEntity.compareState(this.cause) || !mergeEntity.isSafeToMergeInto(this.cause, true))
|
|
+ break;
|
|
+ this.level.mergeHandler.mergeEntity(mergeEntity, this.cause);
|
|
+ }
|
|
+ entities.finishRawIterator();
|
|
+ }
|
|
+
|
|
+ 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 c245760718be113128739101b8038471b7144272..d03c700fffb7b854b3aae08d50419ae406c2ea70 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1798,6 +1798,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
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()) {
|
|
@@ -1820,7 +1826,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
|
|
}
|
|
}
|
|
|
|
- return explosion;
|
|
+ // Sakura - specialised explosions; 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 a8e540c8a27135336fb2d6e37aec13d598b2000e..e64c85bf8fbd3ad0d7e8a6e4a34aed8140f4f8bc 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/item/PrimedTnt.java
|
|
@@ -73,20 +73,7 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak
|
|
|
|
@Override
|
|
public final void respawnEntity(int count) {
|
|
- PrimedTnt tnt = new PrimedTnt(EntityType.TNT, this.level());
|
|
- tnt.updateBukkitHandle(this); // update handle for plugins
|
|
- while (count-- > 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();
|
|
- }
|
|
+ this.mergeData.setCount(count); // Sakura - specialised explosions
|
|
}
|
|
// Sakura end - merge cannon entities
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
index 220d97947df493d1ab825e6637ca648e0b186d18..ceedd8ab94f2f3e2f61b3caf84121dfef4e73bbe 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Explosion.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
|
|
@@ -57,9 +57,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;
|
|
@@ -115,10 +117,10 @@ 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<ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache> blockCache = null;
|
|
+ protected it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache> blockCache = null; // Sakura - private -> protected
|
|
private long[] chunkPosCache = null;
|
|
private net.minecraft.world.level.chunk.LevelChunk[] chunkCache = null;
|
|
- private ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
|
|
+ protected final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z, // Sakura - private -> protected
|
|
final long key, final boolean calculateResistance) {
|
|
ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache ret = this.blockCache.get(key);
|
|
if (ret != null) {
|
|
@@ -216,7 +218,7 @@ public class Explosion {
|
|
(currZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
|
|
ca.spottedleaf.moonrise.patches.collisions.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;
|
|
@@ -370,6 +372,28 @@ public class Explosion {
|
|
return true;
|
|
}
|
|
// Sakura end - optimise explosion protected regions
|
|
+ // Sakura start - specialised explosions
|
|
+ protected final ca.spottedleaf.moonrise.patches.collisions.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 ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
|
+ }
|
|
+
|
|
+ protected final void markBlocksInCacheAsExplodable(List<BlockPos> blocks) {
|
|
+ for (BlockPos blow : blocks) {
|
|
+ ca.spottedleaf.moonrise.patches.collisions.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
|
|
|
|
public static DamageSource getDefaultDamageSource(Level world, @Nullable Entity source) {
|
|
return world.damageSources().explosion(source, Explosion.getIndirectSourceEntityInternal(source));
|
|
@@ -473,16 +497,7 @@ public class Explosion {
|
|
// CraftBukkit end
|
|
this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
|
|
|
|
- // Paper start - collision optimisations
|
|
- 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 ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
|
|
-
|
|
+ final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache = this.createBlockCache(); // Sakura - specialised explosions
|
|
// use initial cache value that is most likely to be used: the source position
|
|
final ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache initialCache;
|
|
{
|
|
@@ -724,7 +739,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
|
|
}
|
|
}
|
|
|
|
@@ -964,7 +982,7 @@ public class Explosion {
|
|
private BlockInteraction() {}
|
|
}
|
|
// Paper start - Optimize explosions
|
|
- private float getBlockDensity(Vec3 vec3d, Entity entity, ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Paper - optimise collisions
|
|
+ protected final float getBlockDensity(Vec3 vec3d, Entity entity, ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[] blockCache, BlockPos.MutableBlockPos blockPos) { // Sakura - private -> protected // Paper - optimise collisions
|
|
// 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 ed049df61d38ed06076273115b841e2852963844..c3260ad0ddb40fbde9c1aaf25c0a2be901357dcc 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -1537,7 +1537,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
}
|
|
|
|
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
|
|
if (configurator != null) configurator.accept(explosion); // Paper - Allow explosions to damage source
|
|
|
|
explosion.explode();
|