diff --git a/paper-api/src/main/java/org/bukkit/event/block/SpongeAbsorbEvent.java b/paper-api/src/main/java/org/bukkit/event/block/SpongeAbsorbEvent.java
index ae56902a84..07495ed6b8 100644
--- a/paper-api/src/main/java/org/bukkit/event/block/SpongeAbsorbEvent.java
+++ b/paper-api/src/main/java/org/bukkit/event/block/SpongeAbsorbEvent.java
@@ -32,12 +32,12 @@ public class SpongeAbsorbEvent extends BlockEvent implements Cancellable {
}
/**
- * Get a list of all blocks to be removed by the sponge.
+ * Get a list of all blocks to be cleared by the sponge.
*
* This list is mutable and contains the blocks in their removed state, i.e.
- * having a type of {@link Material#AIR}.
+ * having a type of {@link Material#AIR} or not waterlogged.
*
- * @return list of the to be removed blocks.
+ * @return list of the cleared blocks.
*/
@NotNull
public List getBlocks() {
diff --git a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
index 690e398fdf..85916b4951 100644
--- a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
+++ b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch
@@ -26353,7 +26353,7 @@ index 302841522cf990c38b1493b716048c0f2db40726..7932a6676db7b652d63be5ae4dcf9bcf
}
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
-index 74c1c6f04a99817e67642cb330534c518830392f..8e9988130d9b912e5b858cd7792bdcefdeb66ada 100644
+index 92a39927d7bd9af3ab67c2595d8ed435a2da6a69..7c9a2eed4441f816723562e0012f918db265912e 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -55,7 +55,7 @@ import net.minecraft.world.level.storage.DimensionDataStorage;
@@ -28633,7 +28633,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896
+ // Paper end - block counting
}
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
-index b1bebf925d4c1f8aa94917cffe878f092b89ad72..cd052ebeb963f1c6e717dfdc0397ec187d15b180 100644
+index a97de8eddd764e628d0f3866c5d1e99e6dae5f6e..503ac3a16c8490d7a620d43b12c4f0c80565615a 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@@ -29761,7 +29761,7 @@ index 300f3ed58109219d97846082941b860585f66fed..9175a7e4e6076626cb46144c5858c2f2
// Paper start - Affects Spawning API
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
-index fa56dd1bdca53f7667dc6b4b2c8634483bae5262..b799d9cc54c45920a7c48d5302454c9cec3ab7c2 100644
+index 1eb8cb187d33510a4e99d229e721a2e7db4012ad..f286dd9996590e5d448ca809c34b6f640203e274 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -81,6 +81,7 @@ import net.minecraft.world.level.storage.LevelData;
@@ -30497,7 +30497,7 @@ index fa56dd1bdca53f7667dc6b4b2c8634483bae5262..b799d9cc54c45920a7c48d5302454c9c
}
// Paper end - Option to prevent armor stands from doing entity lookups
-@@ -994,7 +1642,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
+@@ -987,7 +1635,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
if (this.isOutsideBuildHeight(pos)) {
return null;
} else {
@@ -30506,7 +30506,7 @@ index fa56dd1bdca53f7667dc6b4b2c8634483bae5262..b799d9cc54c45920a7c48d5302454c9c
? null
: this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE);
}
-@@ -1087,22 +1735,16 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
+@@ -1080,22 +1728,16 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
public List getEntities(@Nullable Entity entity, AABB boundingBox, Predicate super Entity> predicate) {
Profiler.get().incrementCounter("getEntities");
List list = Lists.newArrayList();
@@ -30537,7 +30537,7 @@ index fa56dd1bdca53f7667dc6b4b2c8634483bae5262..b799d9cc54c45920a7c48d5302454c9c
}
@Override
-@@ -1116,33 +1758,94 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
+@@ -1109,33 +1751,94 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
this.getEntities(entityTypeTest, bounds, predicate, output, Integer.MAX_VALUE);
}
diff --git a/paper-server/patches/features/0019-Add-Alternate-Current-redstone-implementation.patch b/paper-server/patches/features/0019-Add-Alternate-Current-redstone-implementation.patch
index 5404a4f4ca..fe1796186f 100644
--- a/paper-server/patches/features/0019-Add-Alternate-Current-redstone-implementation.patch
+++ b/paper-server/patches/features/0019-Add-Alternate-Current-redstone-implementation.patch
@@ -2352,10 +2352,10 @@ index f9c96bbdc54e68b9216b7f8662bfae03012d2866..34b7769663e235b93c6388ab0c92c00f
@Override
public void onCreated(Entity entity) {
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
-index b799d9cc54c45920a7c48d5302454c9cec3ab7c2..e4b9a564aad3d9b673808caa18265b06592ceab8 100644
+index f286dd9996590e5d448ca809c34b6f640203e274..c41df4b1fff1f65532256e835dc30fadbb4f8c8b 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
-@@ -2100,6 +2100,17 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
+@@ -2093,6 +2093,17 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl
return 0;
}
diff --git a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch
index c84ba5d6be..348a42a9a9 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/Level.java.patch
@@ -547,23 +547,16 @@
public boolean shouldTickDeath(Entity entity) {
return true;
-@@ -608,6 +_,19 @@
+@@ -608,6 +_,12 @@
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
-+ // CraftBukkit start
-+ return this.getBlockEntity(pos, true);
-+ }
-+
-+ @Nullable
-+ public BlockEntity getBlockEntity(BlockPos pos, boolean validate) {
+ // Paper start - Perf: Optimize capturedTileEntities lookup
+ net.minecraft.world.level.block.entity.BlockEntity blockEntity;
+ if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(pos)) != null) {
+ return blockEntity;
+ }
+ // Paper end - Perf: Optimize capturedTileEntities lookup
-+ // CraftBukkit end
if (this.isOutsideBuildHeight(pos)) {
return null;
} else {
diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch
index 015dae2402..da59d59965 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/block/SpongeBlock.java.patch
@@ -33,7 +33,7 @@
} else {
if (!blockState.is(Blocks.KELP)
&& !blockState.is(Blocks.KELP_PLANT)
-@@ -81,16 +_,55 @@
+@@ -81,16 +_,49 @@
return BlockPos.TraversalNodeStatus.SKIP;
}
@@ -43,7 +43,6 @@
+ // CraftBukkit start
+ // BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null;
+ // dropResources(blockState, level, blockPos, blockEntity);
-+ // level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3);
+ blockList.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3);
+ // CraftBukkit end
}
@@ -67,22 +66,17 @@
+
+ for (org.bukkit.craftbukkit.block.CraftBlockState snapshot : snapshots) {
+ BlockPos blockPos = snapshot.getPosition();
-+ BlockState state = level.getBlockState(blockPos);
-+ FluidState fluid = level.getFluidState(blockPos);
++ BlockState blockState = level.getBlockState(blockPos);
++ FluidState fluidState = level.getFluidState(blockPos);
+
-+ if (fluid.is(FluidTags.WATER)) {
-+ if (state.getBlock() instanceof BucketPickup bucketPickup && !bucketPickup.pickupBlock(null, level, blockPos, state).isEmpty()) {
-+ // NOP
-+ } else if (state.getBlock() instanceof LiquidBlock) {
-+ // NOP
-+ } else if (state.is(Blocks.KELP) || state.is(Blocks.KELP_PLANT) || state.is(Blocks.SEAGRASS) || state.is(Blocks.TALL_SEAGRASS)) {
-+ BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(blockPos) : null;
++ if (!fluidState.is(FluidTags.WATER)) {
++ } else if (blockState.getBlock() instanceof BucketPickup bucketPickup && !bucketPickup.pickupBlock(null, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState).isEmpty()) {
++ } else if (blockState.getBlock() instanceof LiquidBlock) {
++ } else if (blockState.is(Blocks.KELP) || blockState.is(Blocks.KELP_PLANT) || blockState.is(Blocks.SEAGRASS) || blockState.is(Blocks.TALL_SEAGRASS)) {
++ BlockEntity blockEntity = blockState.hasBlockEntity() ? level.getBlockEntity(blockPos) : null;
+
-+ // Paper start - Fix SpongeAbsortEvent handling
-+ if (snapshot.getHandle().isAir()) {
-+ dropResources(state, level, blockPos, blockEntity);
-+ }
-+ // Paper end - Fix SpongeAbsortEvent handling
++ if (snapshot.getHandle().isAir()) {
++ dropResources(blockState, level, blockPos, blockEntity);
+ }
+ }
+ snapshot.place(snapshot.getFlags());
diff --git a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch
index 89e1525889..85e23ecea4 100644
--- a/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/level/portal/PortalShape.java.patch
@@ -140,7 +140,7 @@
BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1))
+ .forEach(pos -> this.blocks.setBlock(pos, blockState, 18));
+ org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent((java.util.List) (java.util.List) this.blocks.getSnapshotBlocks(), bworld, (entity == null) ? null : entity.getBukkitEntity(), org.bukkit.event.world.PortalCreateEvent.CreateReason.FIRE);
-+ level.getMinecraftWorld().getServer().server.getPluginManager().callEvent(event);
++ level.getMinecraftWorld().getServer().server.getPluginManager().callEvent(event); // todo the list is not really mutable here unlike other call and the portal frame is included
+
+ if (event.isCancelled()) {
+ return false;
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
index 1775eb659e..0a10f49ee4 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
@@ -198,7 +198,6 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
BlockPos pos = CraftLocation.toBlockPosition(location);
BlockStateListPopulator populator = new BlockStateListPopulator(this.getHandle());
boolean result = this.generateTree(populator, this.getHandle().getMinecraftWorld().getChunkSource().getGenerator(), pos, new RandomSourceWrapper(random), treeType);
- populator.refreshTiles();
populator.placeSomeBlocks(predicate == null ? ($ -> true) : predicate);
return result;
}
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index b50c982d04..8067e94f06 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -74,10 +74,6 @@ public abstract class CraftBlockEntityState extends Craft
this.loadData(state.getSnapshotNBT());
}
- public void refreshSnapshot() {
- this.load(this.blockEntity);
- }
-
private RegistryAccess getRegistryAccess() {
LevelAccessor worldHandle = this.getWorldHandle();
return (worldHandle != null) ? worldHandle.registryAccess() : CraftRegistry.getMinecraftRegistry();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java
index 13f79e667e..9f699fdbd2 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java
@@ -1,10 +1,9 @@
package org.bukkit.craftbukkit.entity;
import org.bukkit.craftbukkit.CraftServer;
-import org.bukkit.entity.Flying;
import org.bukkit.entity.Ghast;
-public class CraftGhast extends CraftMob implements Ghast, CraftEnemy, Flying {
+public class CraftGhast extends CraftMob implements Ghast, CraftEnemy {
public CraftGhast(CraftServer server, net.minecraft.world.entity.monster.Ghast entity) {
super(server, entity);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
index 99898ce0b3..8ef7b968b4 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java
@@ -4,11 +4,10 @@ import net.minecraft.Optionull;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.CraftLocation;
-import org.bukkit.entity.Flying;
import org.bukkit.entity.Phantom;
import java.util.UUID;
-public class CraftPhantom extends CraftMob implements Phantom, CraftEnemy, Flying {
+public class CraftPhantom extends CraftMob implements Phantom, CraftEnemy {
public CraftPhantom(CraftServer server, net.minecraft.world.entity.monster.Phantom entity) {
super(server, entity);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java
index 6ece55f683..b7c3673814 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java
@@ -19,34 +19,30 @@ import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.storage.LevelData;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.block.CraftBlock;
-import org.bukkit.craftbukkit.block.CraftBlockEntityState;
import org.bukkit.craftbukkit.block.CraftBlockState;
public class BlockStateListPopulator extends DummyGeneratorAccess {
+
private final LevelAccessor world;
- private final Map dataMap = new HashMap<>();
+ private final Map dataMap = new LinkedHashMap<>();
private final Map entityMap = new HashMap<>();
- private final LinkedHashMap blocks;
+
+ private List blocks;
public BlockStateListPopulator(LevelAccessor world) {
- this(world, new LinkedHashMap<>());
- }
-
- private BlockStateListPopulator(LevelAccessor world, LinkedHashMap blocks) {
this.world = world;
- this.blocks = blocks;
}
@Override
public net.minecraft.world.level.block.state.BlockState getBlockState(BlockPos pos) {
- net.minecraft.world.level.block.state.BlockState state = this.dataMap.get(pos);
- return (state != null) ? state : this.world.getBlockState(pos);
+ CapturedBlock block = this.dataMap.get(pos);
+ return (block != null) ? block.state() : this.world.getBlockState(pos);
}
@Override
public FluidState getFluidState(BlockPos pos) {
- net.minecraft.world.level.block.state.BlockState state = this.dataMap.get(pos);
- return (state != null) ? state.getFluidState() : this.world.getFluidState(pos);
+ CapturedBlock block = this.dataMap.get(pos);
+ return (block != null) ? block.state().getFluidState() : this.world.getFluidState(pos);
}
@Override
@@ -63,21 +59,23 @@ public class BlockStateListPopulator extends DummyGeneratorAccess {
public boolean setBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState state, int flags, int recursionLeft) {
pos = pos.immutable();
// remove first to keep insertion order
- this.blocks.remove(pos);
+ this.dataMap.remove(pos);
- this.dataMap.put(pos, state);
- if (state.hasBlockEntity()) {
- this.entityMap.put(pos, ((EntityBlock) state.getBlock()).newBlockEntity(pos, state));
+ this.dataMap.put(pos, new CapturedBlock(state, flags));
+ if (state.getBlock() instanceof EntityBlock entityBlock) {
+ // based on LevelChunk#setBlockState
+ BlockEntity currentBlockEntity = this.getBlockEntity(pos);
+ final BlockEntity newBlockEntity;
+ if (currentBlockEntity != null && currentBlockEntity.isValidBlockState(state)) {
+ newBlockEntity = currentBlockEntity; // previous block entity is still valid for this block state
+ currentBlockEntity.setBlockState(state);
+ } else {
+ newBlockEntity = entityBlock.newBlockEntity(pos, state); // create a new one when the block change
+ }
+ this.entityMap.put(pos, newBlockEntity);
} else {
this.entityMap.put(pos, null);
}
-
- // use 'this' to ensure that the block state is the correct TileState
- CraftBlockState snapshot = (CraftBlockState) CraftBlock.at(this, pos).getState();
- snapshot.setFlags(flags);
- // set world handle to ensure that updated calls are done to the world and not to this populator
- snapshot.setWorldHandle(this.world);
- this.blocks.put(pos, snapshot);
return true;
}
@@ -86,11 +84,19 @@ public class BlockStateListPopulator extends DummyGeneratorAccess {
return this.world.getMinecraftWorld();
}
- public void refreshTiles() {
- for (CraftBlockState snapshot : this.blocks.values()) {
- if (snapshot instanceof CraftBlockEntityState) {
- ((CraftBlockEntityState>) snapshot).refreshSnapshot();
- }
+ @Override
+ public ServerLevel getLevel() {
+ return this.getMinecraftWorld();
+ }
+
+ private void iterateSnapshots(Consumer callback) {
+ for (Map.Entry entry : this.dataMap.entrySet()) {
+ // use 'this' to ensure that the block state is the correct TileState
+ CraftBlockState snapshot = (CraftBlockState) CraftBlock.at(this, entry.getKey()).getState();
+ snapshot.setFlags(entry.getValue().flags());
+ // set world handle to ensure that updated calls are done to the world and not to this populator
+ snapshot.setWorldHandle(this.world);
+ callback.accept(snapshot);
}
}
@@ -107,16 +113,21 @@ public class BlockStateListPopulator extends DummyGeneratorAccess {
}
public void placeSomeBlocks(Consumer super CraftBlockState> beforeRun, Predicate super BlockState> filter) {
- for (CraftBlockState state : this.blocks.values()) {
- if (filter.test(state)) {
- beforeRun.accept(state);
- state.place(state.getFlags());
+ for (CraftBlockState snapshot : this.getSnapshotBlocks()) {
+ if (filter.test(snapshot)) {
+ beforeRun.accept(snapshot);
+ snapshot.place(snapshot.getFlags());
}
}
}
public List getSnapshotBlocks() {
- return new ArrayList<>(this.blocks.values());
+ if (this.blocks == null) {
+ List blocks = new ArrayList<>();
+ this.iterateSnapshots(blocks::add);
+ this.blocks = blocks;
+ }
+ return blocks;
}
// For tree generation
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CapturedBlock.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CapturedBlock.java
new file mode 100644
index 0000000000..c5644b43e4
--- /dev/null
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CapturedBlock.java
@@ -0,0 +1,6 @@
+package org.bukkit.craftbukkit.util;
+
+import net.minecraft.world.level.block.state.BlockState;
+
+public record CapturedBlock(BlockState state, int flags) {
+}