From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Sat, 9 Sep 2023 18:39:15 +0100 Subject: [PATCH] Merge Cannon Entities diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index c5fffb6ca4ef4d447e4eb668e8980fe0885d99da..939264c895f452e909cfbff19eced2cfc904ee6a 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -687,6 +687,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - rewrite chunk system this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit this.levelTickScheduler.registerNewTask(this.explosionPositions::clear, 0); // Sakura - client visibility settings + this.levelTickScheduler.registerNewTask(this.mergeHandler::expire, 200); // Sakura - merge cannon entities } // Paper start @@ -801,6 +802,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR + final Entity[] previousEntity = new Entity[1]; // Sakura - merge cannon entities this.entityTickList .forEach( entity -> { @@ -819,6 +821,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe entity.stopRiding(); } + // Sakura start - merge cannon entities + Entity previous = previousEntity[0]; + if (this.mergeHandler.tryMerge(entity, previous)) { + return; + } else { + previousEntity[0] = entity; + } + // Sakura end - merge cannon entities + profilerFiller.push("tick"); this.guardEntityTick(this::tickNonPassenger, entity); profilerFiller.pop(); diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index cffd3f64b253dba53eacc60c8ae82dd297f98e5b..68e2b26835a2588a047e9ea175eb8e4912041976 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -549,6 +549,27 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return to.entityState() != null && to.entityState().comparePositionAndMotion(this); } // Sakura end - store entity data/state + // Sakura start - merge cannon entities + public final void updateBukkitHandle(Entity entity) { + if (this.bukkitEntity != null) { + this.bukkitEntity.setHandle(entity); + } else { + this.bukkitEntity = entity.getBukkitEntity(); + } + } + + public final long getPackedOriginPosition() { + if (this.origin != null) { + return BlockPos.asLong( + Mth.floor(this.origin.x()), + Mth.floor(this.origin.y()), + Mth.floor(this.origin.z()) + ); + } else { + return Long.MIN_VALUE; + } + } + // Sakura end - merge cannon entities public Entity(EntityType entityType, Level level) { this.type = entityType; @@ -4951,6 +4972,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.removalReason != Entity.RemovalReason.UNLOADED_TO_CHUNK) { this.getPassengers().forEach(Entity::stopRiding); } // Paper - rewrite chunk system this.levelCallback.onRemove(removalReason); this.onRemoval(removalReason); + // Sakura start - merge cannon entities + if (removalReason == RemovalReason.DISCARDED) { + this.level.mergeHandler.removeEntity(this); + } + // Sakura end - merge cannon entities // Paper start - Folia schedulers if (!(this instanceof ServerPlayer) && removalReason != RemovalReason.CHANGED_DIMENSION && !alreadyRemoved) { // Players need to be special cased, because they are regularly removed from the world diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java index 45f08a65d8b423fbf69f66d695bb9d67cf0f2f23..3af6e04bb4dcd91de2794bbc8d3eff16def9efa8 100644 --- a/net/minecraft/world/entity/item/FallingBlockEntity.java +++ b/net/minecraft/world/entity/item/FallingBlockEntity.java @@ -50,7 +50,7 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; -public class FallingBlockEntity extends Entity { +public class FallingBlockEntity extends Entity implements me.samsuik.sakura.entity.merge.MergeableEntity { // Sakura - merge cannon entities private static final Logger LOGGER = LogUtils.getLogger(); private static final BlockState DEFAULT_BLOCK_STATE = Blocks.SAND.defaultBlockState(); private static final int DEFAULT_TIME = 0; @@ -72,12 +72,63 @@ public class FallingBlockEntity extends Entity { public boolean autoExpire = true; // Paper - Expand FallingBlock API public boolean heightParity; // Sakura - falling block height parity api + // Sakura start - merge cannon entities + private final me.samsuik.sakura.entity.merge.MergeEntityData mergeData = new me.samsuik.sakura.entity.merge.MergeEntityData(this); + + @Override + public final me.samsuik.sakura.entity.merge.MergeEntityData getMergeEntityData() { + return this.mergeData; + } + + @Override + public final boolean isSafeToMergeInto(me.samsuik.sakura.entity.merge.MergeableEntity entity, boolean ticksLived) { + return entity instanceof FallingBlockEntity fbe + && fbe.blockState.equals(this.blockState) + && (!ticksLived || fbe.time - 1 == this.time); + } + + @Override + public final void respawnEntity(int count) { + while (count-- >= 1) { + // Unlike PrimedTnt we have to try respawn each stacked entity + FallingBlockEntity fallingBlock = new FallingBlockEntity(EntityType.FALLING_BLOCK, this.level()); + + // Try to stack the falling block + this.entityState().apply(fallingBlock); + fallingBlock.blockState = this.blockState; + fallingBlock.spawnReason = this.spawnReason; + fallingBlock.time = this.time - 1; + fallingBlock.tick(); + + // If you horizontal stack into a moving piston block this condition will be met. + if (!fallingBlock.isRemoved()) { + this.mergeData.count = count + 1; + fallingBlock.storeEntityState(); + fallingBlock.entityState().apply(this); + break; + } else if (count == 0) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); + } + } + } + + @Override + public @Nullable ItemEntity spawnAtLocation(ServerLevel level, net.minecraft.world.level.ItemLike item) { // may be overridden by plugins + ItemEntity itemEntity = null; + for (int i = 0; i < this.mergeData.count; ++i) { + itemEntity = super.spawnAtLocation(level, item); + } + return itemEntity; + } + // Sakura end - merge cannon entities + public FallingBlockEntity(EntityType entityType, Level level) { super(entityType, level); this.dropItem = level.sakuraConfig().cannons.sand.dropItems; // Sakura - configure falling blocks dropping items this.heightParity = level.sakuraConfig().cannons.mechanics.fallingBlockParity; // Sakura - configure cannon mechanics this.isFallingBlock = true; // Sakura - client visibility settings this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement + this.mergeData.mergeLevel = level.sakuraConfig().cannons.mergeLevel; // Sakura - merge cannon entities } public FallingBlockEntity(Level level, double x, double y, double z, BlockState state) { @@ -237,6 +288,7 @@ public class FallingBlockEntity extends Entity { return; } // CraftBukkit end + if (this.tryToRespawnEntity()) return; // Sakura - merge cannon entities if (this.level().setBlock(blockPos, this.blockState, 3)) { ((ServerLevel)this.level()) .getChunkSource() @@ -341,6 +393,7 @@ public class FallingBlockEntity extends Entity { compound.putBoolean("CancelDrop", this.cancelDrop); if (!this.autoExpire) compound.putBoolean("Paper.AutoExpire", false); // Paper - Expand FallingBlock API + compound.putInt("merge_count", this.mergeData.count); // Sakura - merge cannon entities; save to nbt } @Override @@ -356,6 +409,7 @@ public class FallingBlockEntity extends Entity { this.blockData = compound.getCompound("TileEntityData").map(blockData -> this.level().paperConfig().entities.spawning.filterBadTileEntityNbtFromFallingBlocks && this.blockState.getBlock() instanceof net.minecraft.world.level.block.GameMasterBlock ? null : blockData).map(CompoundTag::copy).orElse(null); // Paper - Filter bad block entity nbt data from falling blocks this.cancelDrop = compound.getBooleanOr("CancelDrop", false); this.autoExpire = compound.getBooleanOr("Paper.AutoExpire", true); // Paper - Expand FallingBlock API + this.mergeData.count = compound.getIntOr("merge_count", 1); // Sakura - merge cannon entities; load from nbt } public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) { diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java index 0fbac85dd7b624899db3825149a5d2b167ecedaa..a1cbeb8216edda93ac6043b113314d53e9347fd6 100644 --- a/net/minecraft/world/entity/item/PrimedTnt.java +++ b/net/minecraft/world/entity/item/PrimedTnt.java @@ -35,7 +35,7 @@ import org.bukkit.event.entity.EntityRemoveEvent; import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit end -public class PrimedTnt extends Entity implements TraceableEntity { +public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sakura.entity.merge.MergeableEntity { // Sakura - merge cannon entities private static final EntityDataAccessor DATA_FUSE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_BLOCK_STATE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.BLOCK_STATE); private static final short DEFAULT_FUSE_TIME = 80; @@ -61,11 +61,48 @@ public class PrimedTnt extends Entity implements TraceableEntity { public float explosionPower = 4.0F; public boolean isIncendiary = false; // CraftBukkit + // Sakura start - merge cannon entities + private final me.samsuik.sakura.entity.merge.MergeEntityData mergeData = new me.samsuik.sakura.entity.merge.MergeEntityData(this); + + @Override + public final me.samsuik.sakura.entity.merge.MergeEntityData getMergeEntityData() { + return this.mergeData; + } + + @Override + public final boolean isSafeToMergeInto(me.samsuik.sakura.entity.merge.MergeableEntity entity, boolean ticksLived) { + return entity instanceof PrimedTnt tnt + && tnt.getFuse() + 1 == this.getFuse() + // required to prevent issues with powdered snow + && (tnt.entityState().fallDistance() == this.fallDistance + || tnt.entityState().fallDistance() > 2.5f && this.fallDistance > 2.5f); + } + + @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(); + } + } + // Sakura end - merge cannon entities + public PrimedTnt(EntityType entityType, Level level) { super(entityType, level); this.blocksBuilding = true; this.isPrimedTNT = true; // Sakura - client visibility settings this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement + this.mergeData.mergeLevel = level.sakuraConfig().cannons.mergeLevel; // Sakura - merge cannon entities } public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) { @@ -145,6 +182,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { if (i <= 0) { // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event //this.discard(); + this.tryToRespawnEntity(); // Sakura - merge cannon entities if (!this.level().isClientSide) { this.explode(); } @@ -218,6 +256,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { if (this.explosionPower != 4.0F) { compound.putFloat("explosion_power", this.explosionPower); } + compound.putInt("merge_count", this.mergeData.count); // Sakura - merge cannon entities; save to nbt } @Override @@ -226,6 +265,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { this.setFuse(compound.getShortOr("fuse", (short)80)); this.setBlockState(compound.read("block_state", BlockState.CODEC, registryOps).orElse(DEFAULT_BLOCK_STATE)); this.explosionPower = Mth.clamp(compound.getFloatOr("explosion_power", 4.0F), 0.0F, 128.0F); + this.mergeData.count = compound.getIntOr("merge_count", 1); // Sakura - merge cannon entities; load from nbt } @Nullable diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java index 7bf2b9a5a6c0a5c78e8623e158367552eb253fbc..b5e9a92ddb789344650a0c5c10ffec6db5f90035 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -828,6 +828,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl public final me.samsuik.sakura.listener.LevelTickScheduler levelTickScheduler = new me.samsuik.sakura.listener.LevelTickScheduler(); public final me.samsuik.sakura.listener.BlockChangeTracker blockChangeTracker = new me.samsuik.sakura.listener.BlockChangeTracker(this); // Sakura end - track block changes and tick scheduler + public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities protected Level( WritableLevelData levelData, diff --git a/net/minecraft/world/level/block/BasePressurePlateBlock.java b/net/minecraft/world/level/block/BasePressurePlateBlock.java index 42ee3f32fe44c1f0680c994a69201f7bd7792673..96c977df11c660ccb9a9b32e61c865084e3776ce 100644 --- a/net/minecraft/world/level/block/BasePressurePlateBlock.java +++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java @@ -92,7 +92,7 @@ public abstract class BasePressurePlateBlock extends Block { } private void checkPressed(@Nullable Entity entity, Level level, BlockPos pos, BlockState state, int currentSignal) { - int signalStrength = this.getSignalStrength(level, pos); + int signalStrength = this.getSignalStrength(level, pos, currentSignal == 0); // Sakura - merge cannon entities boolean flag = currentSignal > 0; boolean flag1 = signalStrength > 0; @@ -162,6 +162,12 @@ public abstract class BasePressurePlateBlock extends Block { // CraftBukkit end } + // Sakura start - merge cannon entities + protected int getSignalStrength(Level world, BlockPos pos, boolean entityInside) { + return this.getSignalStrength(world, pos); + } + // Sakura end - merge cannon entities + protected abstract int getSignalStrength(Level level, BlockPos pos); protected abstract int getSignalForState(BlockState state); diff --git a/net/minecraft/world/level/block/WeightedPressurePlateBlock.java b/net/minecraft/world/level/block/WeightedPressurePlateBlock.java index 5e095919828e89d12f2676b3c544842a81e047a1..e47a5010baea87ebf472ccac9ae98ede7a490b50 100644 --- a/net/minecraft/world/level/block/WeightedPressurePlateBlock.java +++ b/net/minecraft/world/level/block/WeightedPressurePlateBlock.java @@ -39,6 +39,11 @@ public class WeightedPressurePlateBlock extends BasePressurePlateBlock { @Override protected int getSignalStrength(Level level, BlockPos pos) { + // Sakura start - merge cannon entities + return this.getSignalStrength(level, pos, false); + } + protected final int getSignalStrength(Level level, BlockPos pos, boolean entityInside) { + // Sakura end - merge cannon entities // CraftBukkit start // int min = Math.min(getEntityCount(level, TOUCH_AABB.move(pos), Entity.class), this.maxWeight); int min = 0; @@ -54,7 +59,7 @@ public class WeightedPressurePlateBlock extends BasePressurePlateBlock { // We only want to block turning the plate on if all events are cancelled if (!cancellable.isCancelled()) { - min++; + min += !entityInside && entity instanceof me.samsuik.sakura.entity.merge.MergeableEntity mergeEntity ? mergeEntity.getMergeEntityData().count : 1; // Sakura - merge cannon entities } } // CraftBukkit end