More more patches, including hopper optimization patch

This commit is contained in:
Nassim Jahnke 2024-04-25 14:07:39 +02:00
parent c42850dbcc
commit 18acb6fc31
10 changed files with 430 additions and 403 deletions

View file

@ -2068,7 +2068,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
@Override
public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
if (!oldState.is(state.getBlock()) && !world.isClientSide) {
- this.updateSurroundingRedstone(world, pos, state, null); // Paper - Optimize redstone
+ // Paper start - optimize redstone - replace call to updatePowerStrength
@ -2098,7 +2098,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
@@ -0,0 +0,0 @@ public class RedStoneWireBlock extends Block {
@Override
public void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
if (!world.isClientSide) {
+ // Paper start - optimize redstone (Alternate Current)
+ // Alternate Current handles breaking of redstone wires in the WireHandler.

View file

@ -2176,8 +2176,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - optimise collisions
private Direction(int id, int idOpposite, int idHorizontal, String name, Direction.AxisDirection direction, Direction.Axis axis, Vec3i vector) {
this.data3d = id;
private Direction(
final int id,
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
@ -2217,7 +2217,7 @@ diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/jav
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
float f = this.getBlockSpeedFactor();
this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f));
@ -2265,7 +2265,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (this.remainingFireTicks <= 0) {
this.setRemainingFireTicks(-this.getFireImmuneTicks());
}
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
private Vec3 collide(Vec3 movement) {
@ -2364,8 +2364,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, List<VoxelShape> collisions) {
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
float f = this.dimensions.width * 0.8F;
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
float f = this.dimensions.width() * 0.8F;
AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
- return BlockPos.betweenClosedStream(axisalignedbb).anyMatch((blockposition) -> {
@ -3028,7 +3028,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return shape.isFullBlock(); // Paper - optimise collisions
}
public boolean propagatesSkylightDown(BlockState state, BlockGetter world, BlockPos pos) {
public void animateTick(BlockState state, Level world, BlockPos pos, RandomSource random) {}
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@ -3194,7 +3194,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - optimise collisions
});
}
// Paper end
// Paper end - unfuck this
diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
@ -3308,22 +3308,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper start - optimise collisions
+ // called with the shape of a VoxelShape, so we can expect the cache to exist
+ final io.papermc.paper.util.collisions.CachedShapeData cache = voxelSet.getOrCreateCachedShapeData();
- for (int i = 0; i < bitSetDiscreteVoxelShape.ySize; i++) {
- for (int j = 0; j < bitSetDiscreteVoxelShape.xSize; j++) {
- int k = -1;
+
+ final int sizeX = cache.sizeX();
+ final int sizeY = cache.sizeY();
+ final int sizeZ = cache.sizeZ();
- for (int l = 0; l <= bitSetDiscreteVoxelShape.zSize; l++) {
- if (bitSetDiscreteVoxelShape.isFullWide(j, i, l)) {
- if (coalesce) {
- if (k == -1) {
- k = l;
- }
- } else {
- callback.consume(j, i, l, j + 1, i + 1, l + 1);
+
+ int indexX;
+ int indexY = 0;
+ int indexZ;
@ -3353,10 +3342,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ } else {
+ // same notes about loop order as the above
+ // this branch is actually important to optimise, as it affects uncached toAabbs() (which affects optimize())
+
- for (int i = 0; i < bitSetDiscreteVoxelShape.ySize; i++) {
- for (int j = 0; j < bitSetDiscreteVoxelShape.xSize; j++) {
- int k = -1;
+ // only clone when we may write to it
+ bitset = bitset.clone();
+
- for (int l = 0; l <= bitSetDiscreteVoxelShape.zSize; l++) {
- if (bitSetDiscreteVoxelShape.isFullWide(j, i, l)) {
- if (coalesce) {
- if (k == -1) {
- k = l;
- }
- } else {
- callback.consume(j, i, l, j + 1, i + 1, l + 1);
+ for (int y = 0; y < sizeY; ++y, indexY += incY) {
+ indexX = indexY;
+ for (int x = 0; x < sizeX; ++x, indexX += incX) {
@ -3365,6 +3365,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ if (firstSetZ == -1) {
+ break;
+ }
+
+ int lastSetZ = io.papermc.paper.util.collisions.FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex);
+ if (lastSetZ == -1) {
+ lastSetZ = endIndex;
}
- } else if (k != -1) {
- int m = j;
@ -3375,14 +3380,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- bitSetDiscreteVoxelShape.clearZStrip(k, l, m + 1, i);
- m++;
+
+ int lastSetZ = io.papermc.paper.util.collisions.FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex);
+ if (lastSetZ == -1) {
+ lastSetZ = endIndex;
}
- while (bitSetDiscreteVoxelShape.isXZRectangleFull(j, m + 1, k, l, n + 1)) {
- for (int o = j; o <= m; o++) {
- bitSetDiscreteVoxelShape.clearZStrip(k, l, o, n + 1);
+ io.papermc.paper.util.collisions.FlatBitsetUtil.clearRange(bitset, firstSetZ, lastSetZ);
+
+ // try to merge neighbouring on the X axis
@ -3393,8 +3390,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
+ ++endX;
+ io.papermc.paper.util.collisions.FlatBitsetUtil.clearRange(bitset, neighbourIdxStart, neighbourIdxEnd);
+ }
+
}
- while (bitSetDiscreteVoxelShape.isXZRectangleFull(j, m + 1, k, l, n + 1)) {
- for (int o = j; o <= m; o++) {
- bitSetDiscreteVoxelShape.clearZStrip(k, l, o, n + 1);
+ // try to merge neighbouring on the Y axis
+
+ int endY; // exclusive

View file

@ -16,14 +16,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|| Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
+ // Paper start - detailed watchdog information
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.push(this.packetListener);
+ try { // Paper end - detailed watchdog information
+ try {
tickablepacketlistener.tick();
+ } finally { // Paper start - detailed watchdog information
+ } finally {
+ net.minecraft.network.protocol.PacketUtils.packetProcessing.pop();
+ } // Paper end - detailed watchdog information
}
// Paper end - Buffer joins to world
} // Paper end - Buffer joins to world
}
diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java
@ -125,7 +125,7 @@ diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/jav
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return this.onGround;
}
@ -169,7 +169,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (this.noPhysics) {
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
} else {
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.level().getProfiler().pop();
}
}
@ -183,7 +183,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
private boolean isStateClimbable(BlockState state) {
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public void setDeltaMovement(Vec3 velocity) {
@ -193,7 +193,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
public void addDeltaMovement(Vec3 velocity) {
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
// Paper end - Fix MC-4
if (this.position.x != x || this.position.y != y || this.position.z != z) {

View file

@ -116,9 +116,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
}
// Paper start - Add fail move event
- boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && (movedWrongly && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb, d0, d1, d2));
+ // Paper start - optimise out extra getCubes
+ boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && movedWrongly;

View file

@ -60,12 +60,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
this.profiler.push(() -> {
return worldserver + " " + worldserver.dimension().location();
String s = String.valueOf(worldserver);
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -0,0 +0,0 @@ public final class ItemStack {
@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder {
}
public ItemStack copy() {
@ -79,11 +79,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // Paper end - Perf: Optimize Hoppers
return ItemStack.EMPTY;
} else {
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
+ ItemStack itemstack = new ItemStack(originalItem ? this.item : this.getItem(), this.count); // Paper - Perf: Optimize Hoppers
itemstack.setPopTime(this.getPopTime());
if (this.tag != null) {
ItemStack itemstack = new ItemStack(this.getItem(), this.count, this.components.copy());
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
@ -157,10 +153,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
boolean flag = false;
- if (!blockEntity.isEmpty()) {
+ int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
+
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hopperss
flag = HopperBlockEntity.ejectItems(world, pos, state, (Container) blockEntity, blockEntity); // CraftBukkit
+ final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
flag = HopperBlockEntity.ejectItems(world, pos, blockEntity);
}
- if (!blockEntity.inventoryFull()) {
@ -368,181 +363,119 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
+ // Paper end - Perf: Optimize Hoppers
+
private static boolean ejectItems(Level world, BlockPos blockposition, BlockState iblockdata, Container iinventory, HopperBlockEntity hopper) { // CraftBukkit
Container iinventory1 = HopperBlockEntity.getAttachedContainer(world, blockposition, iblockdata);
private static boolean ejectItems(Level world, BlockPos pos, HopperBlockEntity blockEntity) {
Container iinventory = HopperBlockEntity.getAttachedContainer(world, pos, blockEntity);
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (HopperBlockEntity.isFullContainer(iinventory1, enumdirection)) {
if (HopperBlockEntity.isFullContainer(iinventory, enumdirection)) {
return false;
} else {
- for (int i = 0; i < iinventory.getContainerSize(); ++i) {
- if (!iinventory.getItem(i).isEmpty()) {
- ItemStack itemstack = iinventory.getItem(i).copy();
- // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
- for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
- ItemStack itemstack = blockEntity.getItem(i);
-
- if (!itemstack.isEmpty()) {
- int j = itemstack.getCount();
- // CraftBukkit start - Call event when pushing items into other inventories
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-
- Inventory destinationInventory;
- // Have to special case large chests as they work oddly
- if (iinventory1 instanceof CompoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
- } else if (iinventory1.getOwner() != null) {
- destinationInventory = iinventory1.getOwner().getInventory();
- if (iinventory instanceof CompoundContainer) {
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
- } else if (iinventory.getOwner() != null) {
- destinationInventory = iinventory.getOwner().getInventory();
- } else {
- destinationInventory = new CraftInventory(iinventory);
- }
-
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack, destinationInventory, true);
- world.getCraftServer().getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- hopper.setItem(i, itemstack);
- hopper.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
- blockEntity.setItem(i, itemstack);
- blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
+ // Paper start - replace logic; MAKE SURE TO CHECK FOR DIFFS ON UPDATES
+ return hopperPush(world, iinventory1, enumdirection, hopper);
+ // for (int i = 0; i < iinventory.getContainerSize(); ++i) {
+ // if (!iinventory.getItem(i).isEmpty()) {
+ // ItemStack itemstack = iinventory.getItem(i).copy();
+ // // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
+
+ // // CraftBukkit start - Call event when pushing items into other inventories
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+
+ // Inventory destinationInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory1 instanceof CompoundContainer) {
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
+ // } else if (iinventory1.getOwner() != null) {
+ // destinationInventory = iinventory1.getOwner().getInventory();
+ // } else {
+ // destinationInventory = new CraftInventory(iinventory);
+ // }
+
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
+ // world.getCraftServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // hopper.setItem(i, itemstack);
+ // hopper.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+ // return false;
+ // }
+ // int origCount = event.getItem().getAmount(); // Spigot
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
// CraftBukkit end
- ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
- // CraftBukkit end
-
- if (itemstack1.isEmpty()) {
- iinventory1.setChanged();
- iinventory.setChanged();
- return true;
- }
+ // if (itemstack1.isEmpty()) {
+ // iinventory1.setChanged();
+ // return true;
+ // }
- itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
- iinventory.setItem(i, itemstack);
-
- itemstack.setCount(j);
- if (j == 1) {
- blockEntity.setItem(i, itemstack);
- }
- }
- }
+ // itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
+ // iinventory.setItem(i, itemstack);
+ // }
+ // }
-
- return false;
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPush(world, iinventory, enumdirection, blockEntity);
+ //for (int i = 0; i < blockEntity.getContainerSize(); ++i) {
+ // ItemStack itemstack = blockEntity.getItem(i);
+
+ // if (!itemstack.isEmpty()) {
+ // int j = itemstack.getCount();
+ // // CraftBukkit start - Call event when pushing items into other inventories
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+
+ // Inventory destinationInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory instanceof CompoundContainer) {
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ // } else if (iinventory.getOwner() != null) {
+ // destinationInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // destinationInventory = new CraftInventory(iinventory);
+ // }
+
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack, destinationInventory, true);
+ // world.getCraftServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // blockEntity.setItem(i, itemstack);
+ // blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
+ // return false;
+ // }
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
+ // // CraftBukkit end
+
+ // if (itemstack1.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
+
+ // itemstack.setCount(j);
+ // if (j == 1) {
+ // blockEntity.setItem(i, itemstack);
+ // }
+ // }
+ //}
+
+ // return false;
+ // Paper end - Perf: Optimize Hoppers
}
}
}
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static boolean isFullContainer(Container inventory, Direction direction) {
- return HopperBlockEntity.getSlots(inventory, direction).allMatch((i) -> {
- ItemStack itemstack = inventory.getItem(i);
return false;
}
}
-
- return itemstack.getCount() >= itemstack.getMaxStackSize();
- });
+ // Paper start - Perf: Optimize Hoppers
+ if (inventory instanceof WorldlyContainer worldlyContainer) {
+ for (final int slot : worldlyContainer.getSlotsForFace(direction)) {
+ final ItemStack stack = inventory.getItem(slot);
+ if (stack.getCount() < stack.getMaxStackSize()) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ for (int slot = 0, max = inventory.getContainerSize(); slot < max; ++slot) {
+ final ItemStack stack = inventory.getItem(slot);
+ if (stack.getCount() < stack.getMaxStackSize()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ // Paper end - Perf: Optimize Hoppers
return true;
}
private static boolean isEmptyContainer(Container inv, Direction facing) {
- return HopperBlockEntity.getSlots(inv, facing).allMatch((i) -> {
- return inv.getItem(i).isEmpty();
- });
+ return allMatch(inv, facing, IS_EMPTY_TEST); // Paper - Perf: Optimize Hoppers
}
public static boolean suckInItems(Level world, Hopper hopper) {
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (iinventory != null) {
Direction enumdirection = Direction.DOWN;
- return HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) ? false : HopperBlockEntity.getSlots(iinventory, enumdirection).anyMatch((i) -> {
- return HopperBlockEntity.a(hopper, iinventory, i, enumdirection, world); // Spigot
- });
+ // Paper start - Perf: Optimize Hoppers
+ skipPullModeEventFire = skipHopperEvents;
+ // merge container isEmpty check and move logic into one loop
+ if (iinventory instanceof WorldlyContainer worldlyContainer) {
+ for (final int slot : worldlyContainer.getSlotsForFace(enumdirection)) {
+ ItemStack item = worldlyContainer.getItem(slot);
+ if (item.isEmpty() || !canTakeItemFromContainer(hopper, iinventory, item, slot, enumdirection)) {
+ continue;
+ }
+ if (hopperPull(world, hopper, iinventory, item, slot)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ for (int slot = 0, max = iinventory.getContainerSize(); slot < max; ++slot) {
+ ItemStack item = iinventory.getItem(slot);
+ if (item.isEmpty() || !canTakeItemFromContainer(hopper, iinventory, item, slot, enumdirection)) {
+ continue;
+ }
+ if (hopperPull(world, hopper, iinventory, item, slot)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // Paper end - Perf: Optimize Hoppers
} else {
Iterator iterator = HopperBlockEntity.getItemsAtAndAbove(world, hopper).iterator();
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
+ @io.papermc.paper.annotation.DoNotUse // Paper - method unused as logic is inlined above
private static boolean a(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
+ @io.papermc.paper.annotation.DoNotUse // Paper - Optimize hoppers
private static boolean tryTakeInItemFromSlot(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
ItemStack itemstack = iinventory.getItem(i);
- if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) {
- ItemStack itemstack1 = itemstack.copy();
- // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) {
- int j = itemstack.getCount();
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
-
@ -556,11 +489,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- sourceInventory = new CraftInventory(iinventory);
- }
-
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
-
- Bukkit.getServer().getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- iinventory.setItem(i, itemstack1);
- iinventory.setItem(i, itemstack);
-
- if (ihopper instanceof HopperBlockEntity) {
- ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
@ -568,58 +501,58 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
-
- return false;
- }
- int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
- // CraftBukkit end
-
- if (itemstack2.isEmpty()) {
- if (itemstack1.isEmpty()) {
- iinventory.setChanged();
- return true;
- }
-
- itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
- iinventory.setItem(i, itemstack1);
+ // Paper start - Perf: Optimize Hoppers; replace pull logic; MAKE SURE TO CHECK FOR DIFFS WHEN UPDATING
+ if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left unused incase reflective plugins
- itemstack.setCount(j);
- if (j == 1) {
- iinventory.setItem(i, itemstack);
- }
+ // Paper start - Perf: Optimize Hoppers
+ return hopperPull(world, ihopper, iinventory, itemstack, i);
+ // ItemStack itemstack1 = itemstack.copy();
+ // // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
+ // // CraftBukkit start - Call event on collection of items from inventories into the hopper
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+ // int j = itemstack.getCount();
+ // // CraftBukkit start - Call event on collection of items from inventories into the hopper
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
+
+ // Inventory sourceInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory instanceof CompoundContainer) {
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ // } else if (iinventory.getOwner() != null) {
+ // sourceInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // sourceInventory = new CraftInventory(iinventory);
+ // }
+ // Inventory sourceInventory;
+ // // Have to special case large chests as they work oddly
+ // if (iinventory instanceof CompoundContainer) {
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ // } else if (iinventory.getOwner() != null) {
+ // sourceInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // sourceInventory = new CraftInventory(iinventory);
+ // }
+
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false);
+
+ // Bukkit.getServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // iinventory.setItem(i, itemstack1);
+ // Bukkit.getServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // iinventory.setItem(i, itemstack);
+
+ // if (ihopper instanceof HopperBlockEntity) {
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+ // }
+ // if (ihopper instanceof HopperBlockEntity) {
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+ // }
+
+ // return false;
+ // }
+ // int origCount = event.getItem().getAmount(); // Spigot
+ // ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+ // // CraftBukkit end
+ // return false;
+ // }
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+ // // CraftBukkit end
+
+ // if (itemstack2.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
+ // if (itemstack1.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
+
+ // itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
+ // iinventory.setItem(i, itemstack1);
+ // itemstack.setCount(j);
+ // if (j == 1) {
+ // iinventory.setItem(i, itemstack);
+ // }
+ // Paper end - Perf: Optimize Hoppers
}
@ -651,74 +584,41 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
flag = true;
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// CraftBukkit end
}
+ // Paper start - Perf: Optimize Hoppers
+ static final AABB HOPPER_ITEM_SUCK_OVERALL = Hopper.SUCK.bounds();
+ static final AABB[] HOPPER_ITEM_SUCK_INDIVIDUAL = Hopper.SUCK.toAabbs().toArray(new AABB[0]);
+ // Paper end - Perf: Optimize Hoppers
+
public static List<ItemEntity> getItemsAtAndAbove(Level world, Hopper hopper) {
- return (List) hopper.getSuckShape().toAabbs().stream().flatMap((axisalignedbb) -> {
- return world.getEntitiesOfClass(ItemEntity.class, axisalignedbb.move(hopper.getLevelX() - 0.5D, hopper.getLevelY() - 0.5D, hopper.getLevelZ() - 0.5D), EntitySelector.ENTITY_STILL_ALIVE).stream();
- }).collect(Collectors.toList());
+ // Paper start - Perf: Optimize Hoppers
+ // eliminate multiple getEntitiesOfClass() but maintain the voxelshape collision by moving
+ // the individual AABB checks into the predicate
+ final double shiftX = hopper.getLevelX() - 0.5D;
+ final double shiftY = hopper.getLevelY() - 0.5D;
+ final double shiftZ = hopper.getLevelZ() - 0.5D;
+ return world.getEntitiesOfClass(ItemEntity.class, HOPPER_ITEM_SUCK_OVERALL.move(shiftX, shiftY, shiftZ), (final Entity entity) -> {
+ if (!entity.isAlive()) { // EntitySelector.ENTITY_STILL_ALIVE
+ return false;
+ }
+
+ for (final AABB aabb : HOPPER_ITEM_SUCK_INDIVIDUAL) {
+ if (aabb.move(shiftX, shiftY, shiftZ).intersects(entity.getBoundingBox())) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+ // Paper end - Perf: Optimize Hoppers
}
@Nullable
public static Container getContainerAt(Level world, BlockPos pos) {
- return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
+ return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true); // Paper - Perf: Optimize Hoppers
- return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
+ return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true);
}
@Nullable
private static Container getContainerAt(Level world, double x, double y, double z) {
private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z) {
+ // Paper start - Perf: Optimize Hoppers
+ return HopperBlockEntity.getContainerAt(world, x, y, z, false);
+ return HopperBlockEntity.getContainerAt(world, pos, state, x, y, z, false);
+ }
+ @Nullable
+ private static Container getContainerAt(Level world, double x, double y, double z, final boolean optimizeEntities) {
+ private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z, boolean optimizeEntities) {
+ // Paper end - Perf: Optimize Hoppers
Object object = null;
BlockPos blockposition = BlockPos.containing(x, y, z);
if ( !world.spigotConfig.hopperCanLoadChunks && !world.hasChunkAt( blockposition ) ) return null; // Spigot
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
Container iinventory = HopperBlockEntity.getBlockContainer(world, pos, state);
- if (iinventory == null) {
+ if (iinventory == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
iinventory = HopperBlockEntity.getEntityContainer(world, x, y, z);
}
- if (object == null) {
- List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
+ if (object == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !iblockdata.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
+ List<Entity> list = world.getEntitiesOfClass((Class)Container.class, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper - Perf: Optimize Hoppers
if (!list.isEmpty()) {
object = (Container) list.get(world.random.nextInt(list.size()));
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Nullable
private static Container getEntityContainer(Level world, double x, double y, double z) {
- List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
+ List<Entity> list = world.getEntitiesOfClass((Class) Container.class, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper - Perf: Optimize hoppers
return !list.isEmpty() ? (Container) list.get(world.random.nextInt(list.size())) : null;
}
private static boolean canMergeItems(ItemStack first, ItemStack second) {
- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameTags(first, second);
+ return first.getCount() < first.getMaxStackSize() && first.is(second.getItem()) && first.getDamageValue() == second.getDamageValue() && ((first.isEmpty() && second.isEmpty()) || java.util.Objects.equals(first.getTag(), second.getTag())); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second);
+ return first.getCount() < first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
}
@Override
@ -727,24 +627,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -0,0 +0,0 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Override
public boolean isEmpty() {
this.unpackLootTable(null);
- return this.getItems().stream().allMatch(ItemStack::isEmpty);
+ // Paper start - Perf: Optimize Hoppers
+ for (final ItemStack itemStack : this.getItems()) {
+ if (!itemStack.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ // Paper end - Perf: Optimize Hoppers
}
@Override
public ItemStack getItem(int slot) {
- this.unpackLootTable(null);
+ if (slot == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers
return this.getItems().get(slot);
return super.getItem(slot);
}

View file

@ -27,70 +27,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
@@ -0,0 +0,0 @@ public class SynchedEntityData {
// CraftBukkit start
public void refresh(ServerPlayer to) {
if (!this.isEmpty()) {
- List<SynchedEntityData.DataValue<?>> list = this.getNonDefaultValues();
+ List<SynchedEntityData.DataValue<?>> list = this.packAll(); // Paper - Update EVERYTHING not just not default
if (list != null) {
+ if (to.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper
to.connection.send(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
+ } // Paper
}
}
}
// CraftBukkit end
- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
+ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
}
@@ -0,0 +0,0 @@ public class SynchedEntityData {
}
}
+ // Paper start
+ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
+ // Because these values can possibly be desynced on the client.
+ @Nullable
+ private List<SynchedEntityData.DataValue<?>> packAll() {
+ if (this.isEmpty()) {
+ return null;
+ }
+
+ List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
+ for (DataItem<?> dataItem : this.itemsById.values()) {
+ public List<SynchedEntityData.DataValue<?>> packAll() {
+ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
+ for (final DataItem<?> dataItem : this.itemsById) {
+ list.add(dataItem.value());
+ }
+
+ return list;
+ }
+
+ // This method should only be used if the data of an entity could have became desynced
+ // due to interactions on the client.
+ public void resendPossiblyDesyncedEntity(ServerPlayer player) {
+ if (this.entity.tracker == null) {
+ return;
+ }
+
+ if (player.getBukkitEntity().canSee(entity.getBukkitEntity())) {
+ net.minecraft.server.level.ServerEntity serverEntity = this.entity.tracker.serverEntity;
+
+ List<net.minecraft.network.protocol.Packet<net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new ArrayList<>();
+ serverEntity.sendPairingData(player, list::add);
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
+ }
+ }
+
+ // This method allows you to specifically resend certain data accessor keys to the client
+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
+ if (!to.getBukkitEntity().canSee(this.entity.getBukkitEntity())) {
+ return;
+ }
+ List<SynchedEntityData.DataValue<?>> values = new ArrayList<>(keys.size());
+ for (EntityDataAccessor<?> key : keys) {
+ SynchedEntityData.DataItem<?> synchedValue = this.getItem(key);
+ values.add(synchedValue.value());
+ }
+
+ to.connection.send(new ClientboundSetEntityDataPacket(this.entity.getId(), values));
+ }
+ // Paper end
+
public static class DataItem<T> {
final EntityDataAccessor<T> accessor;
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@ -100,12 +65,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Paper end - extend Player Interact cancellation
player.getBukkitEntity().updateInventory(); // SPIGOT-2867
+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
@@ -0,0 +0,0 @@ public class ServerPlayerGameMode {
return enuminteractionresult1;
} else {
return InteractionResult.PASS;
}
+ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
+ else if (this.interactResult && this.interactResult != cancelledItem) {
@ -113,12 +78,53 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+ // Paper end - Properly cancel usable items
}
return enuminteractionresult;
// CraftBukkit end
}
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.Connection;
import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.TickablePacketListener;
-import net.minecraft.network.chat.ChatDecorator;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.LastSeenMessages;
@@ -0,0 +0,0 @@ import net.minecraft.network.protocol.PacketUtils;
import net.minecraft.network.protocol.common.ServerboundClientInformationPacket;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.network.protocol.configuration.ConfigurationProtocols;
-import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundBlockChangedAckPacket;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket;
@@ -0,0 +0,0 @@ import net.minecraft.world.level.block.entity.CrafterBlockEntity;
import net.minecraft.world.level.block.entity.JigsawBlockEntity;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.block.entity.StructureBlockEntity;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftItemType;
-import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.util.LazyPlayerSet;
import org.bukkit.craftbukkit.util.Waitable;
@@ -0,0 +0,0 @@ import org.bukkit.event.inventory.InventoryCreativeEvent;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.event.inventory.SmithItemEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
-import org.bukkit.event.player.PlayerAnimationEvent;
-import org.bukkit.event.player.PlayerAnimationType;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
@ -132,7 +138,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
// Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
- ServerGamePacketListenerImpl.this.send(new ClientboundAddEntityPacket(entity));
+ entity.getEntityData().resendPossiblyDesyncedEntity(player); // Paper - The entire mob gets deleted, so resend it.
+ entity.getEntityData().resendPossiblyDesyncedEntityData(player); // Paper - The entire mob gets deleted, so resend it.
ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
}
@ -144,11 +150,126 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
// CraftBukkit end
- player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
+ //player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn Paper - THIS IS NOT NEEDED ANYMORE
- player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
this.sendLevelInfo(player, worldserver1);
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import com.mojang.logging.LogUtils;
+import io.papermc.paper.util.MCUtil;
import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import java.util.Arrays;
@@ -0,0 +0,0 @@ import net.minecraft.network.syncher.SyncedDataHolder;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
-import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -0,0 +0,0 @@ import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.ScoreHolder;
import net.minecraft.world.scores.Team;
-import org.slf4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
-import org.bukkit.Server;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
-import org.bukkit.craftbukkit.event.CraftPortalEvent;
-import org.bukkit.entity.Hanging;
-import org.bukkit.entity.LivingEntity;
-import org.bukkit.entity.Vehicle;
-import org.bukkit.event.entity.EntityCombustByEntityEvent;
-import org.bukkit.event.hanging.HangingBreakByEntityEvent;
-import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
-import org.bukkit.event.vehicle.VehicleEnterEvent;
-import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.entity.Hanging;
+import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Pose;
+import org.bukkit.entity.Vehicle;
import org.bukkit.event.entity.EntityAirChangeEvent;
+import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.entity.EntityCombustEvent;
import org.bukkit.event.entity.EntityDismountEvent;
import org.bukkit.event.entity.EntityDropItemEvent;
@@ -0,0 +0,0 @@ import org.bukkit.event.entity.EntityMountEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityPoseChangeEvent;
import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
+import org.bukkit.event.vehicle.VehicleEnterEvent;
+import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.plugin.PluginManager;
+import org.slf4j.Logger;
// CraftBukkit end
public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, CommandSource, ScoreHolder {
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// CraftBukkit start
public void refreshEntityData(ServerPlayer to) {
- List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
+ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
- if (list != null) {
+ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
}
}
// CraftBukkit end
+ // Paper start
+ // This method should only be used if the data of an entity could have become desynced
+ // due to interactions on the client.
+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
+ if (this.tracker == null) {
+ return;
+ }
+
+ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
+ final net.minecraft.server.level.ServerEntity serverEntity = this.tracker.serverEntity;
+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
+ serverEntity.sendPairingData(player, list::add);
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
+ }
+ }
+
+ // This method allows you to specifically resend certain data accessor keys to the client
+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
+ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
+ return;
+ }
+
+ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
+ for (final EntityDataAccessor<?> key : keys) {
+ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
+ values.add(synchedValue.value());
+ }
+
+ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values));
+ }
+ // Paper end
public boolean equals(Object object) {
return object instanceof Entity ? ((Entity) object).id == this.id : false;
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
@ -174,8 +295,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (playerBucketFishEvent.isCancelled()) {
((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
- ((ServerPlayer) player).connection.send(new ClientboundAddEntityPacket(entity)); // We need to play out these packets as the client assumes the fish is gone
- entity.getEntityData().refresh((ServerPlayer) player); // Need to send data such as the display name to client
+ entity.getEntityData().resendPossiblyDesyncedEntity((ServerPlayer) player); // Paper
- entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client
+ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper
return Optional.of(InteractionResult.FAIL);
}
entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
@ -190,7 +311,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- entityTracker.broadcast(this.getHandle().getAddEntityPacket());
+ // Paper start, resend possibly desynced entity instead of add entity packet
+ for (ServerPlayerConnection playerConnection : entityTracker.seenBy) {
+ this.getHandle().getEntityData().resendPossiblyDesyncedEntity(playerConnection.getPlayer());
+ this.getHandle().getEntityData().resendPossiblyDesyncedEntityData(playerConnection.getPlayer());
+ }
+ // Paper end
}

View file

@ -9,8 +9,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -0,0 +0,0 @@ dependencies {
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
runtimeOnly("com.mysql:mysql-connector-j:8.2.0")
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
implementation("commons-lang:commons-lang:2.6")
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
+ // Paper start - Use Velocity cipher
+ implementation("com.velocitypowered:velocity-native:3.1.2-SNAPSHOT") {
@ -109,7 +109,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
private final Inflater inflater;
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
+ private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private int threshold;
private boolean validateDecompressed;
@ -170,6 +170,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private void setupInflaterInput(ByteBuf buf) {
ByteBuffer byteBuffer;
if (buf.nioBufferCount() > 0) {
@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
}
}
- public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
+ public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) { // Paper - Use Velocity cipher
this.threshold = compressionThreshold;
this.validateDecompressed = rejectsBadPackets;
}
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
@ -207,32 +216,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper - Use Velocity cipher
int i = byteBuf.readableBytes();
if (i < this.threshold) {
VarInt.write(byteBuf2, 0);
byteBuf2.writeBytes(byteBuf);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] bs = new byte[i];
byteBuf.readBytes(bs);
VarInt.write(byteBuf2, bs.length);
if (i > 8388608) {
throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)");
@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
VarInt.write(byteBuf2, 0);
byteBuf2.writeBytes(byteBuf);
} else {
+ if (this.deflater != null) { // Paper - Use Velocity cipher
byte[] bs = new byte[i];
byteBuf.readBytes(bs);
VarInt.write(byteBuf2, bs.length);
@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(byteBuf2, i);
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
+ try {
+ this.compressor.deflate(compatibleIn, byteBuf2);
+ } finally {
+ compatibleIn.release();
+ }
+ }
+ }
this.deflater.reset();
+ // Paper start - Use Velocity cipher
+ return;
+ }
+
+ VarInt.write(byteBuf2, i);
+ final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
+ try {
+ this.compressor.deflate(compatibleIn, byteBuf2);
+ } finally {
+ compatibleIn.release();
+ }
}
}
}
+ @Override
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
+ if (this.compressor != null) {
@ -244,7 +256,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ // size the compressed size will ever be is the input size minus one.
+ // - Uncompressed
+ // This is fairly obvious - we will then have one more than the uncompressed size.
+ int initialBufferSize = msg.readableBytes() + 1;
+ final int initialBufferSize = msg.readableBytes() + 1;
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
+ }
+
@ -256,9 +268,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ if (this.compressor != null) {
+ this.compressor.close();
+ // Paper end - Use Velocity cipher
}
+ }
+ }
+
public int getThreshold() {
return this.threshold;
}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/Connection.java
@ -301,18 +316,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
if (compressionThreshold >= 0) {
+ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
((CompressionDecoder) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold, rejectsBadPackets);
} else {
- this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
+ this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressor, compressionThreshold, rejectsBadPackets)); // Paper - Use Velocity cipher
}
ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
((CompressionEncoder) this.channel.pipeline().get("compress")).setThreshold(compressionThreshold);
if (channelhandler instanceof CompressionDecoder) {
CompressionDecoder packetdecompressor = (CompressionDecoder) channelhandler;
- packetdecompressor.setThreshold(compressionThreshold, rejectsBadPackets);
+ packetdecompressor.setThreshold(compressor, compressionThreshold, rejectsBadPackets); // Paper - Use Velocity cipher
} else {
- this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressionThreshold));
+ this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper - Use Velocity cipher
this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
}
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
packetcompressor.setThreshold(compressionThreshold);
} else {
- this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper - Use Velocity cipher
}
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
} else {
@ -331,7 +350,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+
this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
protected void initChannel(Channel channel) {
Connection.setInitialProtocolAttributes(channel);
try {
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java

View file

@ -40,16 +40,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
}
@Override
public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
+ // Paper start - Perf: optimize dirt and snow spreading
+ net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
+ if (cachedBlockChunk == null) { // Is this needed?
+ return;
+ }
+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
+ // Paper end - Perf: optimize dirt and snow spreading
+ // Paper end - Perf: optimize dirt and snow spreading
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
return;