PaperMC/Remapped-Spigot-Server-Patches/0388-Optimize-Hoppers.patch

606 lines
33 KiB
Diff
Raw Normal View History

2021-06-11 13:56:17 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Apr 2016 22:09:52 -0400
Subject: [PATCH] Optimize Hoppers
* Removes unnecessary extra calls to .update() that are very expensive
* Lots of itemstack cloning removed. Only clone if the item is actually moved
* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index edda2121f8c1046478beaa77030ebb36d403b334..7fbd501d70dccf869a4454e2789a5d68f2e15754 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -585,4 +585,13 @@ public class PaperWorldConfig {
private void entitiesTargetWithFollowRange() {
entitiesTargetWithFollowRange = getBoolean("entities-target-with-follow-range", entitiesTargetWithFollowRange);
}
+
+ public boolean cooldownHopperWhenFull = true;
+ public boolean disableHopperMoveEvents = false;
+ private void hopperOptimizations() {
+ cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull);
+ log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled"));
+ disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents);
+ log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled"));
+ }
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 753e6f609189c589514739bea80007bace3c89d2..7038897b8fb4c18ca97b95a3b24c30b40b62b005 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -131,6 +131,7 @@ import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.entity.HopperBlockEntity;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.dimension.DimensionType;
@@ -1360,6 +1361,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ HopperBlockEntity.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
this.profiler.push(() -> {
return worldserver + " " + worldserver.dimension().location();
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 02bfa4fb8055e60a84e878ffbf18303c0ee25b1d..ac996d581925c8f92832009945c766962e5b51c5 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -538,11 +538,12 @@ public final class ItemStack {
return this.getItem().interactLivingEntity(this, user, entity, hand);
}
- public ItemStack copy() {
- if (this.isEmpty()) {
+ public ItemStack copy() { return cloneItemStack(false); } // Paper
+ public ItemStack cloneItemStack(boolean origItem) { // Paper
+ if (!origItem && this.isEmpty()) { // Paper
return ItemStack.EMPTY;
} else {
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
+ ItemStack itemstack = new ItemStack(origItem ? this.item : this.getItem(), this.count); // Paper
itemstack.setPopTime(this.getPopTime());
if (this.tag != null) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 3e2cd6c7a34c1a792d7346019a8b039d1f4a7c04..6b79f8cd9258af47afa6efa7b1f97c3780be58b0 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -1162,8 +1162,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
return list;
}
- @Override
- public <T extends Entity> List<T> getEntitiesOfClass(Class<? extends T> entityClass, AABB box, @Nullable Predicate<? super T> predicate) {
+ public <T extends Entity> List<T> getEntities(Class<? extends T> oclass, AABB axisalignedbb, @Nullable Predicate<? super T> predicate) { return getEntitiesOfClass(oclass, axisalignedbb, predicate); } // Paper - OBFHELPER
+ @Override public <T extends Entity> List<T> getEntitiesOfClass(Class<? extends T> entityClass, AABB box, @Nullable Predicate<? super T> predicate) {
this.getProfiler().incrementCounter("getEntities");
int i = Mth.floor((box.minX - 2.0D) / 16.0D);
int j = Mth.ceil((box.maxX + 2.0D) / 16.0D);
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 84012c2d12817e657b046bc168cc8eddebcd3831..05fa76c02ce61e26891ad995fe89e925ea086557 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -77,6 +77,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
public void setCurrentChunk(LevelChunk chunk) {
this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;
}
+ static boolean IGNORE_TILE_UPDATES = false;
// Paper end
@Nullable
@@ -155,6 +156,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
public void setChanged() {
if (this.level != null) {
+ if (IGNORE_TILE_UPDATES) return; // Paper
this.blockState = this.level.getBlockState(this.worldPosition);
this.level.blockEntityChanged(this.worldPosition, this);
if (!this.blockState.isAir()) {
diff --git a/src/main/java/net/minecraft/world/level/block/entity/Hopper.java b/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
index f8e4a42bed265822666141683e36e6696694925b..fc8bb72f7d677f65db505016ad6a4cd6146de29f 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
@@ -1,6 +1,7 @@
package net.minecraft.world.level.block.entity;
import javax.annotation.Nullable;
+import net.minecraft.core.BlockPos;
import net.minecraft.world.Container;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
@@ -17,12 +18,13 @@ public interface Hopper extends Container {
return Hopper.SUCK;
}
- @Nullable
+ //@Nullable // Paper - it's annoying
Level getLevel();
+ default BlockPos getBlockPosition() { return new BlockPos(getX(), getY(), getZ()); } // Paper
- double getLevelX();
+ double getLevelX(); default double getX() { return this.getLevelX(); } // Paper - OBFHELPER
- double getLevelY();
+ double getLevelY(); default double getY() { return this.getLevelY(); } // Paper - OBFHELPER
- double getLevelZ();
+ double getLevelZ(); default double getZ() { return this.getLevelZ(); } // Paper - OBFHELPER
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 04b0f0de43dfd95e82d402068da8a97bdb86f758..70718fcbaa6f671061479957b7608f7639dab54b 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -193,6 +193,160 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return false;
}
+ // Paper start - Optimize Hoppers
+ private static boolean skipPullModeEventFire = false;
+ private static boolean skipPushModeEventFire = false;
+ public static boolean skipHopperEvents = false;
+
+ private boolean hopperPush(Container iinventory, Direction enumdirection) {
+ skipPushModeEventFire = skipHopperEvents;
+ boolean foundItem = false;
+ for (int i = 0; i < this.getContainerSize(); ++i) {
+ ItemStack item = this.getItem(i);
+ if (!item.isEmpty()) {
+ foundItem = true;
+ ItemStack origItemStack = item;
+ ItemStack itemstack = origItemStack;
+
+ final int origCount = origItemStack.getCount();
+ final int moved = Math.min(level.spigotConfig.hopperAmount, origCount);
+ origItemStack.setCount(moved);
+
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
+ // Because nothing uses getItem, every event call should end up the same result.
+ if (!skipPushModeEventFire) {
+ itemstack = callPushMoveEvent(iinventory, itemstack);
+ if (itemstack == null) { // cancelled
+ origItemStack.setCount(origCount);
+ return false;
+ }
+ }
+ final ItemStack itemstack2 = addItem(this, iinventory, itemstack, enumdirection);
+ final int remaining = itemstack2.getCount();
+ if (remaining != moved) {
+ origItemStack = origItemStack.cloneItemStack(true);
+ origItemStack.setCount(origCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(origCount - moved + remaining);
+ }
+ this.setItem(i, origItemStack);
+ iinventory.setChanged();
+ return true;
+ }
+ origItemStack.setCount(origCount);
+ }
+ }
+ if (foundItem && level.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown
+ this.setCooldown(level.spigotConfig.hopperTransfer);
+ }
+ return false;
+ }
+
+ private static boolean hopperPull(Hopper ihopper, Container iinventory, ItemStack origItemStack, int i) {
+ ItemStack itemstack = origItemStack;
+ final int origCount = origItemStack.getCount();
+ final Level world = ihopper.getLevel();
+ final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
+ itemstack.setCount(moved);
+
+ if (!skipPullModeEventFire) {
+ itemstack = callPullMoveEvent(ihopper, iinventory, itemstack);
+ if (itemstack == null) { // cancelled
+ origItemStack.setCount(origCount);
+ // Drastically improve performance by returning true.
+ // No plugin could of relied on the behavior of false as the other call
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
+ }
+
+ final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null);
+ final int remaining = itemstack2.getCount();
+ if (remaining != moved) {
+ origItemStack = origItemStack.cloneItemStack(true);
+ origItemStack.setCount(origCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(origCount - moved + remaining);
+ }
+ IGNORE_TILE_UPDATES = true;
+ iinventory.setItem(i, origItemStack);
+ IGNORE_TILE_UPDATES = false;
+ iinventory.setChanged();
+ return true;
+ }
+ origItemStack.setCount(origCount);
+
+ if (world.paperConfig.cooldownHopperWhenFull) {
+ cooldownHopper(ihopper);
+ }
+
+ return false;
+ }
+
+ private ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack) {
+ Inventory destinationInventory = getInventory(iinventory);
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(),
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
+ boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPushModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(this);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static ItemStack callPullMoveEvent(Hopper hopper, Container iinventory, ItemStack itemstack) {
+ Inventory sourceInventory = getInventory(iinventory);
+ Inventory destination = getInventory(hopper);
+
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory,
+ // Mirror is safe as we no plugins ever use this item
+ CraftItemStack.asCraftMirror(itemstack), destination, false);
+ boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPullModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static Inventory getInventory(Container 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 instanceof BlockEntity) {
+ sourceInventory = ((BlockEntity) iinventory).getOwner(false).getInventory();
+ } else {
+ sourceInventory = iinventory.getOwner().getInventory();
+ }
+ return sourceInventory;
+ }
+
+ private static void cooldownHopper(Hopper hopper) {
+ if (hopper instanceof HopperBlockEntity) {
+ ((HopperBlockEntity) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer);
+ } else if (hopper instanceof MinecartHopper) {
+ ((MinecartHopper) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer / 2);
+ }
+ }
+ // Paper end
+
private boolean ejectItems() {
Container iinventory = this.getAttachedContainer();
@@ -204,27 +358,28 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (this.isFullContainer(iinventory, enumdirection)) {
return false;
} else {
- for (int i = 0; i < this.getContainerSize(); ++i) {
+ return hopperPush(iinventory, enumdirection); /* // Paper - disable rest
+ for (int i = 0; i < this.getSize(); ++i) {
if (!this.getItem(i).isEmpty()) {
- ItemStack itemstack = this.getItem(i).copy();
+ ItemStack itemstack = this.getItem(i).cloneItemStack();
// ItemStack itemstack1 = addItem(this, iinventory, this.splitStack(i, 1), enumdirection);
// CraftBukkit start - Call event when pushing items into other inventories
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.removeItem(i, level.spigotConfig.hopperAmount)); // Spigot
+ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(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);
+ if (iinventory instanceof InventoryLargeChest) {
+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
} else {
destinationInventory = iinventory.getOwner().getInventory();
}
InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
- this.getLevel().getCraftServer().getPluginManager().callEvent(event);
+ this.getWorld().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
this.setItem(i, itemstack);
- this.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
+ this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
return false;
}
int origCount = event.getItem().getAmount(); // Spigot
@@ -232,16 +387,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// CraftBukkit end
if (itemstack1.isEmpty()) {
- iinventory.setChanged();
+ iinventory.update();
return true;
}
- itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
+ itemstack.subtract(origCount - itemstack1.getCount()); // Spigot
this.setItem(i, itemstack);
}
}
- return false;
+ return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
}
}
@@ -250,18 +405,54 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return inventory instanceof WorldlyContainer ? IntStream.of(((WorldlyContainer) inventory).getSlotsForFace(side)) : IntStream.range(0, inventory.getContainerSize());
}
- private boolean isFullContainer(Container inv, Direction enumdirection) {
- return getSlots(inv, enumdirection).allMatch((i) -> {
- ItemStack itemstack = inv.getItem(i);
+ private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
- return itemstack.getCount() >= itemstack.getMaxStackSize();
- });
+ private static boolean anyMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
+
+ // Paper end
+
+ private boolean isFullContainer(Container inv, Direction enumdirection) {
+ // Paper start - no streams
+ return allMatch(inv, enumdirection, STACK_SIZE_TEST);
+ // Paper end
}
private static boolean isEmptyContainer(Container inv, Direction facing) {
- return getSlots(inv, facing).allMatch((i) -> {
- return inv.getItem(i).isEmpty();
- });
+ return allMatch(inv, facing, IS_EMPTY_TEST);
}
public static boolean suckInItems(Hopper hopper) {
@@ -270,9 +461,17 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (iinventory != null) {
Direction enumdirection = Direction.DOWN;
- return isEmptyContainer(iinventory, enumdirection) ? false : getSlots(iinventory, enumdirection).anyMatch((i) -> {
- return tryTakeInItemFromSlot(hopper, iinventory, i, enumdirection);
+ // Paper start - optimize hoppers and remove streams
+ skipPullModeEventFire = skipHopperEvents;
+ return !isEmptyContainer(iinventory, enumdirection) && anyMatch(iinventory, enumdirection, (item, i) -> {
+ // Logic copied from below to avoid extra getItem calls
+ if (!item.isEmpty() && canTakeItem(iinventory, item, i, enumdirection)) {
+ return hopperPull(hopper, iinventory, item, i);
+ } else {
+ return false;
+ }
});
+ // Paper end
} else {
Iterator iterator = getItemsAtAndAbove(hopper).iterator();
@@ -290,47 +489,48 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
- private static boolean tryTakeInItemFromSlot(Hopper hopper, Container inventory, int slot, Direction side) {
+ private static boolean tryTakeInItemFromSlot(Hopper hopper, Container inventory, int slot, Direction side) {// Paper - method unused as logic is inlined above
ItemStack itemstack = inventory.getItem(slot);
- if (!itemstack.isEmpty() && canTakeItemFromContainer(inventory, itemstack, slot, side)) {
- ItemStack itemstack1 = itemstack.copy();
+ if (!itemstack.isEmpty() && canTakeItemFromContainer(inventory, itemstack, slot, side)) { // If this logic changes, update above. this is left inused incase reflective plugins
+ return hopperPull(hopper, inventory, itemstack, slot); /* // Paper - disable rest
+ ItemStack itemstack1 = itemstack.cloneItemStack();
// ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null);
// CraftBukkit start - Call event on collection of items from inventories into the hopper
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(inventory.removeItem(slot, hopper.getLevel().spigotConfig.hopperAmount)); // Spigot
+ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot
Inventory sourceInventory;
// Have to special case large chests as they work oddly
- if (inventory instanceof CompoundContainer) {
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) inventory);
+ if (iinventory instanceof InventoryLargeChest) {
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
} else {
- sourceInventory = inventory.getOwner().getInventory();
+ sourceInventory = iinventory.getOwner().getInventory();
}
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), hopper.getOwner().getInventory(), false);
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
- hopper.getLevel().getCraftServer().getPluginManager().callEvent(event);
+ ihopper.getWorld().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
- inventory.setItem(slot, itemstack1);
+ iinventory.setItem(i, itemstack1);
- if (hopper instanceof HopperBlockEntity) {
- ((HopperBlockEntity) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer); // Spigot
- } else if (hopper instanceof MinecartHopper) {
- ((MinecartHopper) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer / 2); // Spigot
+ if (ihopper instanceof TileEntityHopper) {
+ ((TileEntityHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot
+ } else if (ihopper instanceof EntityMinecartHopper) {
+ ((EntityMinecartHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot
}
return false;
}
int origCount = event.getItem().getAmount(); // Spigot
- ItemStack itemstack2 = addItem(inventory, hopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+ ItemStack itemstack2 = addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
// CraftBukkit end
if (itemstack2.isEmpty()) {
- inventory.setChanged();
+ iinventory.update();
return true;
}
- itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
- inventory.setItem(slot, itemstack1);
+ itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot
+ iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
return false;
@@ -339,7 +539,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(Container inventory, ItemEntity itemEntity) {
boolean flag = false;
// CraftBukkit start
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation
itemEntity.level.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
@@ -381,6 +581,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return !inventory.canPlaceItem(slot, stack) ? false : !(inventory instanceof WorldlyContainer) || ((WorldlyContainer) inventory).canPlaceItemThroughFace(slot, stack, side);
}
+ private static boolean canTakeItem(Container iinventory, ItemStack itemstack, int i, Direction enumdirection) { return canTakeItemFromContainer(iinventory, itemstack, i, enumdirection); } // Paper - OBFHELPER
private static boolean canTakeItemFromContainer(Container inv, ItemStack stack, int slot, Direction facing) {
return !(inv instanceof WorldlyContainer) || ((WorldlyContainer) inv).canTakeItemThroughFace(slot, stack, facing);
}
@@ -393,7 +594,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
boolean flag1 = to.isEmpty();
if (itemstack1.isEmpty()) {
+ IGNORE_TILE_UPDATES = true; // Paper
to.setItem(slot, stack);
+ IGNORE_TILE_UPDATES = false; // Paper
stack = ItemStack.EMPTY;
flag = true;
} else if (canMergeItems(itemstack1, stack)) {
@@ -444,20 +647,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static List<ItemEntity> getItemsAtAndAbove(Hopper ihopper) {
- return (List) ihopper.getSuckShape().toAabbs().stream().flatMap((axisalignedbb) -> {
- return ihopper.getLevel().getEntitiesOfClass(ItemEntity.class, axisalignedbb.move(ihopper.getLevelX() - 0.5D, ihopper.getLevelY() - 0.5D, ihopper.getLevelZ() - 0.5D), EntitySelector.ENTITY_STILL_ALIVE).stream();
- }).collect(Collectors.toList());
+ // Paper start - Optimize item suck in. remove streams, restore 1.12 checks. Seriously checking the bowl?!
+ Level world = ihopper.getLevel();
+ double d0 = ihopper.getX();
+ double d1 = ihopper.getY();
+ double d2 = ihopper.getZ();
+ AABB bb = new AABB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D);
+ return world.getEntities(ItemEntity.class, bb, Entity::isAlive);
+ // Paper end
}
@Nullable
public static Container getContainerAt(Level world, BlockPos blockposition) {
- return getContainerAt(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D);
+ return a(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, true); // Paper
}
@Nullable
- public static Container getContainerAt(Level world, double x, double y, double z) {
+ public static Container getContainerAt(Level world, double x, double y, double z) { return a(world, x, y, z, false); } // Paper - overload to default false
+ public static Container a(Level world, double d0, double d1, double d2, boolean optimizeEntities) { // Paper
Object object = null;
- BlockPos blockposition = new BlockPos(x, y, z);
+ BlockPos blockposition = new BlockPos(d0, d1, d2);
if ( !world.hasChunkAt( blockposition ) ) return null; // Spigot
BlockState iblockdata = world.getBlockState(blockposition);
Block block = iblockdata.getBlock();
@@ -475,8 +684,8 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
- 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 || !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding())) { // Paper
+ List<Entity> list = world.getEntities((Entity) null, new AABB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
if (!list.isEmpty()) {
object = (Container) list.get(world.random.nextInt(list.size()));
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index 5ad419941ff1113ef29b9a4593f44d8f35ba8424..4525032232b5a89de13c6a46dc489a07428e3f21 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -97,12 +97,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Override
public boolean isEmpty() {
this.unpackLootTable((Player) null);
- return this.getItems().stream().allMatch(ItemStack::isEmpty);
+ // Paper start
+ for (ItemStack itemStack : this.getItems()) {
+ if (!itemStack.isEmpty()) {
+ return false;
+ }
+ }
+ // Paper end
+ return true;
}
@Override
public ItemStack getItem(int slot) {
- this.unpackLootTable((Player) null);
+ if (slot == 0) this.unpackLootTable((Player) null); // Paper
return (ItemStack) this.getItems().get(slot);
}