9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2026-01-06 15:41:49 +00:00

Half way through feature patches

This commit is contained in:
Samsuik
2025-01-16 23:48:05 +00:00
parent 1e345a18b5
commit 7dbdd69077
25 changed files with 1445 additions and 4 deletions

4
.gitignore vendored
View File

@@ -13,7 +13,7 @@ paper-api-generator
build.gradle.kts.rej
sakura-api/build.gradle.kts
sakura-server/build.gradle.kts
src/minecraft/
src/vanilla/
sakura-server/src/minecraft/
sakura-server/src/vanilla/
!gradle/wrapper/gradle-wrapper.jar

View File

@@ -0,0 +1,164 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Mon, 22 Apr 2024 23:01:26 +0100
Subject: [PATCH] Replace explosion density cache
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index ae1fad06d85de83f53884449cff21fc0ae62bf97..6d8c513e78fa4efd8c7f6f534cf3958d46448efb 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1751,6 +1751,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
serverLevel.explosionPositions.clear(); // Sakura - client visibility settings
serverLevel.mergeHandler.expire(currentTick); // Sakura - merge cannon entities
+ serverLevel.densityCache.invalidate(); // Sakura - explosion density cache
}
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index a4d7c95514c3db5799efe178efee796413d1bac8..4dd2c4110d85bb7e6575b325196329900d719c64 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -838,6 +838,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
// Paper end - optimise random ticking
public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities
+ public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache
protected Level(
WritableLevelData levelData,
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 9f06e32101f494d94d9210210255d5d72ca4ff36..6d3e03b3c9fed817808de6ee08b531069d342dd5 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -296,7 +296,12 @@ public class ServerExplosion implements Explosion {
Math.fma(dz, diffZ, offZ)
);
- if (!this.clipsAnything(from, source, context, blockCache, blockPos)) {
+ // Sakura start - replace density cache
+ final float density = this.level.densityCache.getKnownDensity(from);
+ if (density != me.samsuik.sakura.explosion.density.BlockDensityCache.UNKNOWN_DENSITY) {
+ missedRays += (int) density;
+ } else if (!this.clipsAnything(from, source, context, blockCache, blockPos)) {
+ // Sakura end - replace density cache
++missedRays;
}
}
@@ -385,8 +390,16 @@ public class ServerExplosion implements Explosion {
double d9 = Mth.lerp(d6, boundingBox.minY, boundingBox.maxY);
double d10 = Mth.lerp(d7, boundingBox.minZ, boundingBox.maxZ);
Vec3 vec3 = new Vec3(d8 + d3, d9, d10 + d4);
- if (entity.level().clip(new ClipContext(vec3, explosionVector, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType()
- == HitResult.Type.MISS) {
+ // Sakura start - replace density cache
+ final net.minecraft.world.phys.HitResult.Type hitResult;
+ final float density = entity.level().densityCache.getKnownDensity(vec3);
+ 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;
+ } else {
+ hitResult = entity.level().clip(new ClipContext(vec3, explosionVector, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, entity)).getType();
+ }
+ if (hitResult == HitResult.Type.MISS) {
+ // Sakura end - replace density cache
i++;
}
@@ -691,6 +704,11 @@ public class ServerExplosion implements Explosion {
return;
}
// CraftBukkit end
+ // Sakura start - explosion density cache
+ if (!blocks.isEmpty() && !this.level.paperConfig().environment.optimizeExplosions) {
+ this.level.densityCache.invalidate();
+ }
+ // Sakura end - explosion density cache
for (BlockPos blockPos : blocks) {
// CraftBukkit start - TNTPrimeEvent
@@ -857,14 +875,12 @@ public class ServerExplosion implements Explosion {
// Paper start - Optimize explosions
protected float getBlockDensity(Vec3 vec3d, Entity entity) {
- if (!this.level.paperConfig().environment.optimizeExplosions) {
- return this.getSeenFraction(vec3d, entity, this.directMappedBlockCache, this.mutablePos); // Paper - collision optimisations
- }
- CacheKey key = new CacheKey(this, entity.getBoundingBox());
- Float blockDensity = this.level.explosionDensityCache.get(key);
- if (blockDensity == null) {
+ // 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, this.directMappedBlockCache, this.mutablePos); // Paper - collision optimisations
- this.level.explosionDensityCache.put(key, blockDensity);
+ this.level.densityCache.putDensity(vec3d, entity, blockDensity);
+ // Sakura end - replace density cache
}
return blockDensity;
diff --git a/net/minecraft/world/level/block/BasePressurePlateBlock.java b/net/minecraft/world/level/block/BasePressurePlateBlock.java
index 69d490c79e30fb42da69bbd804ecaea7b88fe7b0..497409e04dc4b9366da1fbe0641b8424c727a176 100644
--- a/net/minecraft/world/level/block/BasePressurePlateBlock.java
+++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java
@@ -109,6 +109,11 @@ public abstract class BasePressurePlateBlock extends Block {
// CraftBukkit end
if (currentSignal != signalStrength) {
BlockState blockState = this.setSignalForState(state, signalStrength);
+ // Sakura start - explosion density cache
+ if (!level.paperConfig().environment.optimizeExplosions) {
+ level.densityCache.invalidate();
+ }
+ // Sakura end - explosion density cache
level.setBlock(pos, blockState, 2);
this.updateNeighbours(level, pos);
level.setBlocksDirty(pos, state, blockState);
diff --git a/net/minecraft/world/level/block/TripWireHookBlock.java b/net/minecraft/world/level/block/TripWireHookBlock.java
index 6a7e5a642e2eaf7d5dffadb81738f7385a38c0af..dd2ceb8fd24281a6dd06084145f387cff55c6cc9 100644
--- a/net/minecraft/world/level/block/TripWireHookBlock.java
+++ b/net/minecraft/world/level/block/TripWireHookBlock.java
@@ -182,6 +182,11 @@ public class TripWireHookBlock extends Block {
if (!cancelledReceiverHook) { // always trigger two events even when the first hook current change is cancelled
// Paper end - Call BlockRedstoneEvent
Direction opposite = direction.getOpposite();
+ // Sakura start - explosion density cache
+ if (!level.paperConfig().environment.optimizeExplosions) {
+ level.densityCache.invalidate();
+ }
+ // Sakura end - explosion density cache
level.setBlock(blockPosx, blockState1.setValue(FACING, opposite), 3);
notifyNeighbors(block, level, blockPosx, opposite);
emitState(level, blockPosx, flag2, flag3, flag, flag1);
diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java
index 85148858db1fd5e9da8bbdde4b0d84110d80e373..23d5a2726f6113eaa79196ac6f6b8db85e386f97 100644
--- a/net/minecraft/world/phys/AABB.java
+++ b/net/minecraft/world/phys/AABB.java
@@ -442,4 +442,28 @@ public class AABB {
center.x - xSize / 2.0, center.y - ySize / 2.0, center.z - zSize / 2.0, center.x + xSize / 2.0, center.y + ySize / 2.0, center.z + zSize / 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
}

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Fri, 3 May 2024 15:18:58 +0100
Subject: [PATCH] Optimise explosions in protected regions
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 6d3e03b3c9fed817808de6ee08b531069d342dd5..76697d12a12eeedbaab6aef169080320791aff6e 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -345,6 +345,22 @@ public class ServerExplosion implements Explosion {
return rays;
}
// Sakura end - optimise paper explosions
+ // Sakura start - optimise explosion protected regions
+ protected final boolean isRegionUnprotected() {
+ // optimisation: We check if a plugin has cancelled the event or cleared the blockList.
+ // It tells us if the result was thrown away, so we can avoid the block searching logic.
+ // As a side effect the event is called twice which may interfere with some plugins.
+ if (this.source != null && this.level.sakuraConfig().cannons.explosion.optimiseProtectedRegions) {
+ Location location = new Location(this.level.getWorld(), this.center.x, this.center.y, this.center.z);
+ List<org.bukkit.block.Block> blocks = new ObjectArrayList<>(1);
+ blocks.add(location.getBlock());
+ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blocks, 0.0f, this.blockInteraction);
+ return !event.isCancelled() && !event.blockList().isEmpty();
+ }
+
+ return true;
+ }
+ // Sakura end - optimise explosion protected regions
public ServerExplosion(
ServerLevel level,
@@ -449,6 +465,11 @@ public class ServerExplosion implements Explosion {
return ret;
}
// Sakura end - optimise paper explosions
+ // Sakura start - optimise protected explosions
+ if (!this.isRegionUnprotected()) {
+ return ret;
+ }
+ // Sakura end - optimise protected 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

View File

@@ -0,0 +1,165 @@
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/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 83bd1ddec7edb09e9a28eead178d7d07cbde8749..ee44ba44773f245d351aac9461bd6cff18204f01 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1866,7 +1866,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
case STANDARD -> Explosion.BlockInteraction.DESTROY; // CraftBukkit - handle custom explosion type
};
Vec3 vec3 = new Vec3(x, y, z);
- ServerExplosion serverExplosion = new ServerExplosion(this, source, damageSource, damageCalculator, vec3, radius, fire, blockInteraction);
+ // Sakura start - specialised explosions
+ final ServerExplosion serverExplosion;
+ if (source instanceof net.minecraft.world.entity.item.PrimedTnt tnt) {
+ serverExplosion = new me.samsuik.sakura.explosion.special.TntExplosion(this, tnt, damageSource, damageCalculator, vec3, radius, fire, blockInteraction, self -> {
+ this.notifyPlayersOfExplosion(self, self.center(), smallExplosionParticles, largeExplosionParticles, explosionSound);
+ });
+ } else {
+ serverExplosion = new ServerExplosion(this, source, damageSource, damageCalculator, vec3, radius, fire, blockInteraction);
+ }
+ // Sakura end - specialised explosions
if (configurator != null) configurator.accept(serverExplosion);// Paper - Allow explosions to damage source
serverExplosion.explode();
// CraftBukkit start
@@ -1874,6 +1883,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return serverExplosion;
}
// CraftBukkit end
+ // Sakura start - specialised explosions
+ this.notifyPlayersOfExplosion(serverExplosion, vec3, smallExplosionParticles, largeExplosionParticles, explosionSound);
+ return serverExplosion;
+ }
+
+ private void notifyPlayersOfExplosion(ServerExplosion serverExplosion, Vec3 vec3,
+ ParticleOptions smallExplosionParticles, ParticleOptions largeExplosionParticles,
+ Holder<SoundEvent> explosionSound) {
+ // Sakura end - specialised explosions
ParticleOptions particleOptions = serverExplosion.isSmall() ? smallExplosionParticles : largeExplosionParticles;
for (ServerPlayer serverPlayer : this.players) {
@@ -1894,7 +1912,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
}
}
- return serverExplosion; // CraftBukkit
+ // Sakura - specialised explosions; return moved up into explode0
}
private Explosion.BlockInteraction getDestroyType(GameRules.Key<GameRules.BooleanValue> decayGameRule) {
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
index b378b4c4930c4ebd55795591aca173fd1fee46c9..c88fe6c244f6a88f1e42822dd0795187dcc3b655 100644
--- a/net/minecraft/world/entity/item/PrimedTnt.java
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
@@ -77,20 +77,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/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 76697d12a12eeedbaab6aef169080320791aff6e..0285e19d6265056fb5ff9855a5dd41bd16bbc082 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -361,6 +361,38 @@ public class ServerExplosion implements Explosion {
return true;
}
// Sakura end - optimise explosion protected regions
+ // Sakura start - specialised explosions
+ protected final void createBlockCache() {
+ // 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];
+ this.directMappedBlockCache = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
+ this.mutablePos = new BlockPos.MutableBlockPos();
+ // Paper end - collision optimisations
+ }
+
+ protected final void markBlocksInCacheAsExplodable(List<BlockPos> explodedPositions) {
+ for (BlockPos blow : explodedPositions) {
+ 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;
+ }
+ }
+ }
+
+ protected final void clearBlockCache() {
+ // Paper start - collision optimisations
+ this.blockCache = null;
+ this.chunkPosCache = null;
+ this.chunkCache = null;
+ this.directMappedBlockCache = null;
+ this.mutablePos = null;
+ // Paper end - collision optimisations
+ }
+ // Sakura end - specialised explosions
public ServerExplosion(
ServerLevel level,
@@ -670,7 +702,10 @@ public class ServerExplosion implements Explosion {
if (entity instanceof Player) {
Player player = (Player)entity;
if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback
- this.hitPlayers.put(player, vec3);
+ // Sakura start - specialised explosions; tally player velocity
+ final Vec3 explosionImpact = vec3;
+ this.hitPlayers.compute(player, (p, v) -> v != null ? v.add(explosionImpact) : explosionImpact);
+ // Sakura end - specialised explosions; tally player velocity
}
}
@@ -779,14 +814,7 @@ public class ServerExplosion implements Explosion {
return;
}
// CraftBukkit end
- // 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];
- this.directMappedBlockCache = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
- this.mutablePos = new BlockPos.MutableBlockPos();
- // Paper end - collision optimisations
+ this.createBlockCache(); // Sakura - specialised explosions
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
this.hurtEntities();
@@ -800,13 +828,7 @@ public class ServerExplosion implements Explosion {
if (this.fire) {
this.createFire(list);
}
- // Paper start - collision optimisations
- this.blockCache = null;
- this.chunkPosCache = null;
- this.chunkCache = null;
- this.directMappedBlockCache = null;
- this.mutablePos = null;
- // Paper end - collision optimisations
+ this.clearBlockCache(); // Sakura - specialised explosions
}
private static void addOrAppendStack(List<ServerExplosion.StackCollector> stackCollectors, ItemStack stack, BlockPos pos) {

View File

@@ -0,0 +1,220 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Fri, 13 Oct 2023 14:36:19 +0100
Subject: [PATCH] Optimise cannon entity movement
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 2a617bd6d5d14cd69b149d6c5f82f8b2c3bc2d5d..8c3e0ca06f89e4d8c08d30272475cdeaca20b3ef 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1141,6 +1141,75 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return this.moveStartZ;
}
// Paper end - detailed watchdog information
+ // Sakura start - optimise cannon entity movement; stripped back movement method
+ public final void moveStripped(MoverType movementType, Vec3 movement) {
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main");
+ if (this.noPhysics) {
+ this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
+ } else {
+ if (movementType == MoverType.PISTON) {
+ movement = this.limitPistonMovement(movement);
+ if (movement.equals(Vec3.ZERO)) {
+ return;
+ }
+ }
+
+ ProfilerFiller gameprofilerfiller = Profiler.get();
+ gameprofilerfiller.push("move");
+ if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) {
+ movement = movement.multiply(this.stuckSpeedMultiplier);
+ this.stuckSpeedMultiplier = Vec3.ZERO;
+ this.setDeltaMovement(Vec3.ZERO);
+ }
+
+ Vec3 vec3d1 = this.sakura_collide(movement);
+ double d0 = vec3d1.lengthSqr();
+
+ if (d0 > 1.0E-7D || movement.lengthSqr() - d0 < 1.0E-7D) {
+ if (this.fallDistance != 0.0F && d0 >= 1.0D && !this.isFallingBlock) {
+ BlockHitResult clipResult = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this));
+ if (clipResult.getType() != HitResult.Type.MISS) {
+ this.resetFallDistance();
+ }
+ }
+
+ this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z);
+ }
+
+ gameprofilerfiller.pop();
+ gameprofilerfiller.push("rest");
+ boolean movedX = !Mth.equal(movement.x, vec3d1.x);
+ boolean movedZ = !Mth.equal(movement.z, vec3d1.z);
+
+ this.horizontalCollision = movedX || movedZ;
+ this.verticalCollision = movement.y != vec3d1.y;
+ this.verticalCollisionBelow = this.verticalCollision && movement.y < 0.0D;
+
+ this.setOnGroundWithMovement(this.verticalCollisionBelow, this.horizontalCollision, vec3d1);
+ BlockPos blockPosBelow = this.getOnPosLegacy();
+ BlockState blockstate = this.level().getBlockState(blockPosBelow);
+
+ this.checkFallDamage(vec3d1.y, this.onGround(), blockstate, blockPosBelow);
+ if (this.isRemoved()) {
+ gameprofilerfiller.pop();
+ } else {
+ if (this.horizontalCollision) {
+ Vec3 vec3d2 = this.getDeltaMovement();
+ this.setDeltaMovement(movedX ? 0.0D : vec3d2.x, vec3d2.y, movedZ ? 0.0D : vec3d2.z);
+ }
+
+ Block block = blockstate.getBlock();
+ if (movement.y != vec3d1.y) { // remove y momentum
+ block.updateEntityMovementAfterFallOn(this.level(), this);
+ }
+
+ float f = this.getBlockSpeedFactor();
+ this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f));
+ gameprofilerfiller.pop();
+ }
+ }
+ }
+ // Sakura end - optimise cannon entity movement; stripped back movement method
public void move(MoverType type, Vec3 movement) {
final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity
@@ -1488,6 +1557,107 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return distance;
}
+ // Sakura start - optimise cannon entity movement
+ private Vec3 sakura_collide(Vec3 movement) {
+ if (movement.x == 0.0 && movement.y == 0.0 && movement.z == 0.0) {
+ return movement;
+ }
+
+ List<VoxelShape> potentialCollisionsVoxel = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(0);
+ List<AABB> potentialCollisionsBB = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(4);
+ AABB currBoundingBox = this.getBoundingBox();
+
+ if (movement.lengthSqr() >= 12.0) { // axis scan on large movement
+ return this.collideAxisScan(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB);
+ } else {
+ return this.collideCube(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB);
+ }
+ }
+
+ private Vec3 collideCube(Vec3 movement, AABB currBoundingBox, List<VoxelShape> voxelList, List<AABB> bbList) {
+ final AABB bb;
+ if (movement.x() == 0.0 && movement.z() == 0.0) {
+ if (movement.y > 0.0) {
+ bb = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutUpwards(currBoundingBox, movement.y);
+ } else {
+ bb = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutDownwards(currBoundingBox, movement.y);
+ }
+ } else {
+ bb = currBoundingBox.expandTowards(movement.x, movement.y, movement.z);
+ }
+ this.collectCollisions(bb, voxelList, bbList);
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, voxelList, bbList);
+ }
+
+ private Vec3 collideAxisScan(Vec3 movement, AABB currBoundingBox, List<VoxelShape> voxelList, List<AABB> bbList) {
+ double x = movement.x;
+ double y = movement.y;
+ double z = movement.z;
+
+ boolean xSmaller = Math.abs(x) < Math.abs(z);
+
+ if (y != 0.0) {
+ y = this.scanY(currBoundingBox, y, voxelList, bbList);
+ if (y != 0.0) {
+ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetY(currBoundingBox, y);
+ }
+ }
+
+ if (xSmaller && z != 0.0) {
+ z = this.scanZ(currBoundingBox, z, voxelList, bbList);
+ if (z != 0.0) {
+ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetZ(currBoundingBox, z);
+ }
+ }
+
+ if (x != 0.0) {
+ x = this.scanX(currBoundingBox, x, voxelList, bbList);
+ if (x != 0.0) {
+ currBoundingBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.offsetX(currBoundingBox, x);
+ }
+ }
+
+ if (!xSmaller && z != 0.0) {
+ z = this.scanZ(currBoundingBox, z, voxelList, bbList);
+ }
+
+ return new Vec3(x, y, z);
+ }
+
+ private void collectCollisions(AABB collisionBox, List<VoxelShape> voxelList, List<AABB> bbList) {
+ // Copied from the collide method below
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder(
+ this.level, this, collisionBox, voxelList, bbList,
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), null // Sakura - load chunks on movement
+ );
+
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getEntityHardCollisions(
+ this.level, this, collisionBox, bbList, 0, null
+ );
+ }
+
+ private double scanX(AABB currBoundingBox, double x, List<VoxelShape> voxelList, List<AABB> bbList) {
+ AABB scanBox = currBoundingBox.expandTowards(x, 0.0, 0.0);
+ this.collectCollisions(scanBox, voxelList, bbList);
+ x = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsX(currBoundingBox, x, bbList);
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsX(currBoundingBox, x, voxelList);
+ }
+
+ private double scanY(AABB currBoundingBox, double y, List<VoxelShape> voxelList, List<AABB> bbList) {
+ AABB scanBox = currBoundingBox.expandTowards(0.0, y, 0.0);
+ this.collectCollisions(scanBox, voxelList, bbList);
+ y = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsY(currBoundingBox, y, bbList);
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsY(currBoundingBox, y, voxelList);
+ }
+
+ private double scanZ(AABB currBoundingBox, double z, List<VoxelShape> voxelList, List<AABB> bbList) {
+ AABB scanBox = currBoundingBox.expandTowards(0.0, 0.0, z);
+ this.collectCollisions(scanBox, voxelList, bbList);
+ z = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performAABBCollisionsZ(currBoundingBox, z, bbList);
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performVoxelCollisionsZ(currBoundingBox, z, voxelList);
+ }
+ // Sakura end - optimise cannon entity movement
+
// Paper start - optimise collisions
protected Vec3 collide(Vec3 movement) {
final boolean xZero = movement.x == 0.0;
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
index 1d9afcf995ee734f13803e26956439e5c3450f44..c6836ab9a2789520931d2119aeebeaf2179f27fa 100644
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -213,7 +213,7 @@ public class FallingBlockEntity extends Entity implements me.samsuik.sakura.enti
Block block = this.blockState.getBlock();
this.time++;
this.applyGravity();
- this.move(MoverType.SELF, this.getDeltaMovement());
+ this.moveStripped(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement
this.applyEffectsFromBlocks();
// Paper start - Configurable falling blocks height nerf
if (this.level().paperConfig().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) {
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
index c88fe6c244f6a88f1e42822dd0795187dcc3b655..2ee04093d7c8b61a48913bd4c929528e357aa971 100644
--- a/net/minecraft/world/entity/item/PrimedTnt.java
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
@@ -148,7 +148,7 @@ public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sak
// Sakura - remove max tnt per tick
this.handlePortal();
this.applyGravity();
- this.move(MoverType.SELF, this.getDeltaMovement());
+ this.moveStripped(MoverType.SELF, this.getDeltaMovement()); // Sakura - optimise cannon entity movement
this.applyEffectsFromBlocks();
// Paper start - Configurable TNT height nerf
if (this.level().paperConfig().fixes.tntEntityHeightNerf.test(v -> this.getY() > v)) {

View File

@@ -0,0 +1,117 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Thu, 27 Jun 2024 17:02:32 +0100
Subject: [PATCH] Add maxSearch to getEntities
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
index 39148fe540d616cdd4d63c4d1a02b422cab0f6eb..50a551d5bcac9b2b6644d7b43e625185090657a6 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
@@ -314,7 +314,14 @@ public final class ChunkEntitySlices {
public boolean getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
- return this.allEntities.getEntitiesLimited(except, box, into, predicate, maxCount);
+ // Sakura start - add maxSearch to getEntities
+ return this.getEntities(except, box, into, predicate, maxCount, Integer.MAX_VALUE);
+ }
+
+ public boolean getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
+ final int maxCount, final int maxSearch) {
+ return this.allEntities.getEntitiesLimited(except, box, into, predicate, maxCount, maxSearch);
+ // Sakura end - add maxSearch to getEntities
}
public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
@@ -570,6 +577,13 @@ public final class ChunkEntitySlices {
public boolean getEntitiesLimited(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
+ // Sakura start - add maxSearch to getEntities
+ return this.getEntitiesLimited(except, box, into, predicate, maxCount, Integer.MAX_VALUE);
+ }
+
+ public boolean getEntitiesLimited(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
+ final int maxCount, final int maxSearch) {
+ // Sakura end - add maxSearch to getEntities
if (this.count == 0) {
return false;
}
@@ -591,8 +605,14 @@ public final class ChunkEntitySlices {
final Entity[] storage = list.storage;
- for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
- final Entity entity = storage[i];
+ // Sakura start - add maxSearch to getEntities
+ final int len = Math.min(storage.length, list.size());
+ final int offset = this.slices.world.random.nextInt(len);
+ for (int i = 0; i < len; ++i) {
+ final int pos = (i + offset) % len;
+ final Entity entity = storage[pos];
+ if (i > maxSearch) break;
+ // Sakura end - add maxSearch to getEntities
if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
continue;
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
index 7554c109c35397bc1a43dd80e87764fd78645bbf..d60f30f7afb15cc90c1bd4b816136d00b23a53e4 100644
--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
@@ -722,6 +722,13 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
+ // Sakura start - add maxSearch to getEntities
+ this.getEntities(except, box, into, predicate, maxCount, Integer.MAX_VALUE);
+ }
+
+ public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
+ final int maxCount, final int maxSearch) {
+ // Sakura end - add maxSearch to getEntities
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
@@ -753,7 +760,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
continue;
}
- if (chunk.getEntities(except, box, into, predicate, maxCount)) {
+ if (chunk.getEntities(except, box, into, predicate, maxCount, maxSearch)) { // Sakura - add maxSearch to getEntities
return;
}
}
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 4dd2c4110d85bb7e6575b325196329900d719c64..b4a8a81f1fa091e45f1f39fdb69c61871d7dc6b9 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1791,10 +1791,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
this.getEntities(entityTypeTest, bounds, predicate, output, Integer.MAX_VALUE);
}
- // Paper start - rewrite chunk system
+ // Sakura start - add maxSearch to getEntities
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
final AABB boundingBox, final Predicate<? super T> predicate,
final List<? super T> into, final int maxCount) {
+ this.getEntities(entityTypeTest, boundingBox, predicate, into, maxCount, Integer.MAX_VALUE);
+ }
+
+ // Paper start - rewrite chunk system
+ public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
+ final AABB boundingBox, final Predicate<? super T> predicate,
+ final List<? super T> into, final int maxCount, final int maxSearch) {
+ // Sakura end - add maxSearch to getEntities
Profiler.get().incrementCounter("getEntities");
if (entityTypeTest instanceof net.minecraft.world.entity.EntityType<T> byType) {
@@ -1811,7 +1819,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
if (entityTypeTest == null) {
if (maxCount != Integer.MAX_VALUE) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities((Entity)null, boundingBox, (List)into, (Predicate)predicate, maxCount);
+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities((Entity)null, boundingBox, (List)into, (Predicate)predicate, maxCount, maxSearch); // Sakura - add maxSearch to getEntities
ca.spottedleaf.moonrise.common.PlatformHooks.get().addToGetEntities((Level)(Object)this, entityTypeTest, boundingBox, predicate, into, maxCount);
return;
} else {

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Thu, 23 Sep 2021 18:50:13 +0100
Subject: [PATCH] Use maxEntityCollision limit for entity retrieval
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 7ed355448ff6fbcf585d82a88d456908f9eb3ae6..e7b92681e5d65892da00699cc29c713219c9db8c 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3640,7 +3640,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
return;
}
// Paper end - don't run getEntities if we're not going to use its result
- List<Entity> entities = this.level().getEntities(this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule)); // Paper - Climbing should not bypass cramming gamerule
+ // Sakura start - use maxEntityCollision limit for entity retrieval
+ int limit = Math.max(_int, this.level().paperConfig().collisions.maxEntityCollisions);
+ int search = limit * limit;
+ List<Entity> entities = new ArrayList<>();
+ this.level().getEntities(null, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule), entities, limit, search); // Paper - Climbing should not bypass cramming gamerule
+ // Sakura end - use maxEntityCollision limit for entity retrieval
if (!entities.isEmpty()) {
// Paper - don't run getEntities if we're not going to use its result; moved up
if (_int > 0 && entities.size() > _int - 1 && this.random.nextInt(4) == 0) {

View File

@@ -0,0 +1,105 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Wed, 15 Nov 2023 23:18:38 +0000
Subject: [PATCH] Explosion Durable Blocks
diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java
index 68e50c6ade879d263424f244070677cb81c34c33..8467af4ee57b6699227370ada7bf15ca41fb91c3 100644
--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -45,8 +45,31 @@ public class BlockItem extends Item {
this.block = block;
}
+ // Sakura start - explosion durable blocks
+ private void sendBlockDurabilityToPlayer(UseOnContext context) {
+ Player player = context.getPlayer();
+ BlockState state = context.getLevel().getBlockState(context.getClickedPos());
+ Block block = state.getBlock();
+ me.samsuik.sakura.explosion.durable.DurableMaterial material = context.getLevel().localConfig().config(context.getClickedPos()).durableMaterials.get(block);
+
+ if (material != null) {
+ int remaining = context.getLevel().durabilityManager.durability(context.getClickedPos(), material);
+ int durability = material.durability();
+
+ player.getBukkitEntity().sendRichMessage(
+ me.samsuik.sakura.configuration.GlobalConfiguration.get().messages.durableBlockInteraction,
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.unparsed("remaining", String.valueOf(remaining)),
+ net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.unparsed("durability", String.valueOf(durability))
+ );
+ }
+ }
+
@Override
public InteractionResult useOn(UseOnContext context) {
+ if (this.getBlock() == net.minecraft.world.level.block.Blocks.POTATOES && context.getPlayer() != null) {
+ this.sendBlockDurabilityToPlayer(context);
+ }
+ // Sakura end - explosion durable blocks
InteractionResult interactionResult = this.place(new BlockPlaceContext(context));
return !interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.CONSUMABLE)
? super.use(context.getLevel(), context.getPlayer(), context.getHand())
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index b4a8a81f1fa091e45f1f39fdb69c61871d7dc6b9..19aa5010b019e343d0fb085359eac98bcb5b5efa 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -839,6 +839,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
// Paper end - optimise random ticking
public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities
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
protected Level(
WritableLevelData levelData,
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 0285e19d6265056fb5ff9855a5dd41bd16bbc082..58c14cc2d041348f42e8466fc93f18736c2618da 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -133,7 +133,7 @@ public class ServerExplosion implements Explosion {
BlockState blockState = ((ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk)chunk).moonrise$getBlock(x, y, z);
FluidState fluidState = blockState.getFluidState();
- Optional<Float> resistance = !calculateResistance ? Optional.empty() : this.damageCalculator.getBlockExplosionResistance((Explosion)(Object)this, this.level, pos, blockState, fluidState);
+ Optional<Float> resistance = !calculateResistance ? Optional.empty() : this.calculateBlockResistance(blockState, fluidState, pos); // Sakura - explosion durable blocks
ret = new ca.spottedleaf.moonrise.patches.collisions.ExplosionBlockCache(
key, pos, blockState, fluidState,
@@ -393,6 +393,20 @@ public class ServerExplosion implements Explosion {
// Paper end - collision optimisations
}
// Sakura end - specialised explosions
+ // Sakura start - explosion durable blocks
+ private Optional<Float> 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(this, this.level, pos, blockState, fluidState);
+ }
+ // Sakura end - explosion durable blocks
public ServerExplosion(
ServerLevel level,
@@ -779,6 +793,16 @@ public class ServerExplosion implements Explosion {
}
}
// CraftBukkit end
+ // Sakura start - explosion durable blocks
+ if (this.level.sakuraConfig().cannons.explosion.allowNonTntBreakingDurableBlocks || this.source instanceof net.minecraft.world.entity.item.PrimedTnt) {
+ me.samsuik.sakura.explosion.durable.DurableMaterial material = this.level.localConfig().config(blockposition).durableMaterials.get(block);
+
+ if (material != null && material.durability() >= 0 && !this.level.durabilityManager.damage(blockposition, material)) {
+ iterator.remove();
+ continue;
+ }
+ }
+ // Sakura end - explosion durable blocks
this.level
.getBlockState(blockPos)

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samsuik <kfian294ma4@gmail.com>
Date: Thu, 16 Nov 2023 00:59:04 +0000
Subject: [PATCH] Destroy Waterlogged Blocks
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
index 58c14cc2d041348f42e8466fc93f18736c2618da..f74aee504fa048a43272a3c3e9d1c6f9fa4a5913 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -402,6 +402,11 @@ public class ServerExplosion implements 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
+ if (!fluidState.isEmpty() && !blockState.liquid() && this.level.sakuraConfig().cannons.explosion.destroyWaterloggedBlocks) {
+ return Optional.of(ZERO_RESISTANCE);
+ }
+ // Sakura end - destroy water logged blocks
}
return this.damageCalculator.getBlockExplosionResistance(this, this.level, pos, blockState, fluidState);

View File

@@ -0,0 +1,23 @@
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) {

View File

@@ -0,0 +1,62 @@
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<DensityData> 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 invalidate() {
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;
}
}

View File

@@ -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());
}
}

View File

@@ -0,0 +1,43 @@
package me.samsuik.sakura.explosion.durable;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import net.minecraft.core.BlockPos;
import java.util.concurrent.TimeUnit;
public final class DurableBlockManager {
private final Cache<BlockPos, DurableBlock> durableBlocks = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.MINUTES)
.maximumSize(65534)
.build();
public boolean damage(BlockPos pos, DurableMaterial material) {
DurableBlock block = this.durableBlocks.getIfPresent(pos);
if (block == null) {
this.durableBlocks.put(pos, block = new DurableBlock(material.durability()));
}
return block.damage();
}
public int durability(BlockPos pos, DurableMaterial material) {
final DurableBlock block = this.durableBlocks.getIfPresent(pos);
return block != null ? block.durability() : material.durability();
}
private static final class DurableBlock {
private int durability;
public DurableBlock(int durability) {
this.durability = durability;
}
public int durability() {
return this.durability;
}
public boolean damage() {
return --this.durability <= 0;
}
}
}

View File

@@ -0,0 +1,203 @@
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 it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ExplosionDamageCalculator;
import net.minecraft.world.level.ServerExplosion;
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 ServerExplosion {
private static final double ENTITY_DISPATCH_DISTANCE = Math.pow(32.0, 2.0);
protected final T cause; // preferred over source
private Vec3 impactPosition;
protected final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
private final Consumer<SpecialisedExplosion<T>> applyEffects;
public SpecialisedExplosion(ServerLevel level, T entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 center, float power, boolean createFire, BlockInteraction destructionType, Consumer<SpecialisedExplosion<T>> applyEffects) {
super(level, entity, damageSource, behavior, center, power, createFire, destructionType);
this.cause = entity;
this.impactPosition = center;
this.applyEffects = applyEffects;
}
protected double getExplosionOffset() {
return (double) this.cause.getBbHeight() * 0.0625D;
}
protected abstract int getExplosionCount();
protected abstract void startExplosion();
@Override
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 = this.getExplosionCount() - 1; i >= 0; --i) {
this.finalizeExplosionAndParticles(List.of());
}
} else {
this.createBlockCache();
this.startExplosion(); // search for blocks, impact entities, finalise if necessary
this.clearBlockCache();
}
}
protected final boolean requiresImpactEntities(List<BlockPos> blocks, Vec3 center) {
if (this.impactPosition.distanceToSqr(center) > ENTITY_DISPATCH_DISTANCE) {
this.impactPosition = center;
return true;
}
return !blocks.isEmpty();
}
protected final boolean finalizeExplosionAndParticles(List<BlockPos> blocks) {
this.wasCanceled = false;
List<BlockPos> explodedPositions = new ObjectArrayList<>(blocks);
this.interactWithBlocks(explodedPositions);
if (!this.wasCanceled) {
this.applyEffects.accept(this);
this.getHitPlayers().clear();
}
return !explodedPositions.isEmpty();
}
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.directMappedBlockCache, null);
}
protected final void recalculateExplosionPosition() {
this.recalculateExplosionPosition(this.cause);
}
protected final void recalculateExplosionPosition(T entity) {
double x = entity.getX();
double y = entity.getY() + this.getExplosionOffset();
double z = entity.getZ();
this.center = new Vec3(x, y, 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((float) radius, 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); // Paper - Optimize 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);
}
}

View File

@@ -0,0 +1,201 @@
package me.samsuik.sakura.explosion.special;
import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet;
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.server.level.ServerLevel;
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.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.craftbukkit.util.CraftVector;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Consumer;
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(ServerLevel level, PrimedTnt tnt, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 center, float power, boolean createFire, BlockInteraction destructionType, Consumer<SpecialisedExplosion<PrimedTnt>> applyEffects) {
super(level, tnt, damageSource, behavior, center, power, createFire, destructionType, applyEffects);
this.originalPosition = center;
this.bounds = new AABB(center, center);
}
@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;
List<BlockPos> toBlow = this.midExplosion(previousMomentum, lastCycle); // search for blocks and impact entities
boolean destroyedBlocks = this.finalizeExplosionAndParticles(toBlow);
if (!lastCycle) {
EntityState entityState = this.nextSourceVelocity();
this.postExplosion(toBlow, destroyedBlocks); // update wrapped, clear recent block cache
this.updateExplosionPosition(entityState, destroyedBlocks);
}
}
}
private List<BlockPos> midExplosion(Vec3 previousMomentum, boolean lastCycle) {
final List<BlockPos> explodedPositions;
if (this.wrapped < FOUND_ALL_BLOCKS) {
explodedPositions = this.calculateExplodedPositions();
} else {
explodedPositions = List.of();
}
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.center);
this.explosions.add(this.center);
if (lastCycle || this.requiresImpactEntities(explodedPositions, this.center)) {
this.locateAndImpactEntitiesInBounds();
this.bounds = new AABB(this.center, this.center);
this.explosions.clear();
}
return explodedPositions;
}
@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 >= ALL_DIRECTIONS) {
if (!destroyedBlocks && this.level().sakuraConfig().cannons.explosion.avoidRedundantBlockSearches) {
this.wrapped++;
} else {
this.wrapped = ALL_DIRECTIONS;
}
}
}
private Vector getCauseOrigin() {
Vector origin = this.cause.getOriginVector();
return origin == null ? CraftVector.toBukkit(this.center) : 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.center, 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.center.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.center.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.getFirst(), 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);
}
}
}

Submodule sakura-server/src/minecraft/java updated: 51af06117f...6e26d2a9a1

Submodule sakura-server/src/minecraft/resources updated: e35daf625d...6f9aaa646d