Apply the updated patches
This commit is contained in:
683
paper-server/patches/features/0017-Optimize-Hoppers.patch
Normal file
683
paper-server/patches/features/0017-Optimize-Hoppers.patch
Normal file
@@ -0,0 +1,683 @@
|
||||
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 by tracking changes to the event via an internal event implementation.
|
||||
* 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/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdfe19136a2
|
||||
--- /dev/null
|
||||
+++ b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java
|
||||
@@ -0,0 +1,29 @@
|
||||
+package io.papermc.paper.event.inventory;
|
||||
+
|
||||
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
+import org.bukkit.inventory.Inventory;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
+import org.jspecify.annotations.NullMarked;
|
||||
+
|
||||
+@NullMarked
|
||||
+public class PaperInventoryMoveItemEvent extends InventoryMoveItemEvent {
|
||||
+
|
||||
+ public boolean calledSetItem;
|
||||
+ public boolean calledGetItem;
|
||||
+
|
||||
+ public PaperInventoryMoveItemEvent(final Inventory sourceInventory, final ItemStack itemStack, final Inventory destinationInventory, final boolean didSourceInitiate) {
|
||||
+ super(sourceInventory, itemStack, destinationInventory, didSourceInitiate);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ItemStack getItem() {
|
||||
+ this.calledGetItem = true;
|
||||
+ return super.getItem();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setItem(final ItemStack itemStack) {
|
||||
+ super.setItem(itemStack);
|
||||
+ this.calledSetItem = true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index d450d4af96716caff4b29a84d1d83ec4010854f0..8657e4fd7c5e0e23b69d9a982408a7d038f0a787 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1563,6 +1563,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
for (ServerLevel serverLevel : this.getAllLevels()) {
|
||||
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
|
||||
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
|
||||
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
|
||||
profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().location());
|
||||
/* Drop global time updates
|
||||
if (this.tickCount % 20 == 0) {
|
||||
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
|
||||
index 50cd12def88c9449cad8875c553f5ed9ef1cd791..3d93bb1aac5ad4830fc1dceddb6bebacee28f72a 100644
|
||||
--- a/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/net/minecraft/world/item/ItemStack.java
|
||||
@@ -815,10 +815,16 @@ public final class ItemStack implements DataComponentHolder {
|
||||
}
|
||||
|
||||
public ItemStack copy() {
|
||||
- if (this.isEmpty()) {
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return this.copy(false);
|
||||
+ }
|
||||
+
|
||||
+ public ItemStack copy(final boolean originalItem) {
|
||||
+ if (!originalItem && this.isEmpty()) {
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
return EMPTY;
|
||||
} else {
|
||||
- ItemStack itemStack = new ItemStack(this.getItem(), this.count, this.components.copy());
|
||||
+ ItemStack itemStack = new ItemStack(originalItem ? this.item : this.getItem(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers
|
||||
itemStack.setPopTime(this.getPopTime());
|
||||
return itemStack;
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
index 2ebdf1ad323bb53dfe9eed319e25856b35a1443c..77618757c0e678532dbab814aceed83f7f1cd892 100644
|
||||
--- a/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/BlockEntity.java
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public abstract class BlockEntity {
|
||||
+ static boolean ignoreBlockEntityUpdates; // Paper - Perf: Optimize Hoppers
|
||||
// CraftBukkit start - data containers
|
||||
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
|
||||
public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer;
|
||||
@@ -196,6 +197,7 @@ public abstract class BlockEntity {
|
||||
|
||||
public void setChanged() {
|
||||
if (this.level != null) {
|
||||
+ if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers
|
||||
setChanged(this.level, this.worldPosition, this.blockState);
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
index 60e1e44f328e66d52ebf08476b533fef83bc5eba..eb02249b518c2d262315c4cd5965bec5d81166a1 100644
|
||||
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
@@ -139,18 +139,56 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ private static final int HOPPER_EMPTY = 0;
|
||||
+ private static final int HOPPER_HAS_ITEMS = 1;
|
||||
+ private static final int HOPPER_IS_FULL = 2;
|
||||
+
|
||||
+ private static int getFullState(final HopperBlockEntity hopper) {
|
||||
+ hopper.unpackLootTable(null);
|
||||
+
|
||||
+ final List<ItemStack> hopperItems = hopper.items;
|
||||
+
|
||||
+ boolean empty = true;
|
||||
+ boolean full = true;
|
||||
+
|
||||
+ for (int i = 0, len = hopperItems.size(); i < len; ++i) {
|
||||
+ final ItemStack stack = hopperItems.get(i);
|
||||
+ if (stack.isEmpty()) {
|
||||
+ full = false;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!full) {
|
||||
+ // can't be full
|
||||
+ return HOPPER_HAS_ITEMS;
|
||||
+ }
|
||||
+
|
||||
+ empty = false;
|
||||
+
|
||||
+ if (stack.getCount() != stack.getMaxStackSize()) {
|
||||
+ // can't be full or empty
|
||||
+ return HOPPER_HAS_ITEMS;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return empty ? HOPPER_EMPTY : (full ? HOPPER_IS_FULL : HOPPER_HAS_ITEMS);
|
||||
+ }
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
+
|
||||
private static boolean tryMoveItems(Level level, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier validator) {
|
||||
if (level.isClientSide) {
|
||||
return false;
|
||||
} else {
|
||||
if (!blockEntity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) {
|
||||
boolean flag = false;
|
||||
- if (!blockEntity.isEmpty()) {
|
||||
+ final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers
|
||||
+ if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers
|
||||
flag = ejectItems(level, pos, blockEntity);
|
||||
}
|
||||
|
||||
- if (!blockEntity.inventoryFull()) {
|
||||
- flag |= validator.getAsBoolean();
|
||||
+ if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers
|
||||
+ flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
@@ -174,6 +212,206 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
return true;
|
||||
}
|
||||
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ public static boolean skipHopperEvents;
|
||||
+ private static boolean skipPullModeEventFire;
|
||||
+ private static boolean skipPushModeEventFire;
|
||||
+
|
||||
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
|
||||
+ skipPushModeEventFire = skipHopperEvents;
|
||||
+ boolean foundItem = false;
|
||||
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
|
||||
+ final ItemStack item = hopper.getItem(i);
|
||||
+ if (!item.isEmpty()) {
|
||||
+ foundItem = true;
|
||||
+ ItemStack origItemStack = item;
|
||||
+ ItemStack movedItem = origItemStack;
|
||||
+
|
||||
+ final int originalItemCount = origItemStack.getCount();
|
||||
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
||||
+ origItemStack.setCount(movedItemCount);
|
||||
+
|
||||
+ // 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) {
|
||||
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
|
||||
+ if (movedItem == null) { // cancelled
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
|
||||
+ final int remainingItemCount = remainingItem.getCount();
|
||||
+ if (remainingItemCount != movedItemCount) {
|
||||
+ origItemStack = origItemStack.copy(true);
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ if (!origItemStack.isEmpty()) {
|
||||
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
||||
+ }
|
||||
+ hopper.setItem(i, origItemStack);
|
||||
+ destination.setChanged();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ }
|
||||
+ }
|
||||
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
|
||||
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) {
|
||||
+ ItemStack movedItem = origItemStack;
|
||||
+ final int originalItemCount = origItemStack.getCount();
|
||||
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
||||
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
|
||||
+ movedItem.setCount(movedItemCount);
|
||||
+
|
||||
+ if (!skipPullModeEventFire) {
|
||||
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
|
||||
+ if (movedItem == null) { // cancelled
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ // Drastically improve performance by returning true.
|
||||
+ // No plugin could have relied on the behavior of false as the other call
|
||||
+ // site for IMIE did not exhibit the same behavior
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
|
||||
+ final int remainingItemCount = remainingItem.getCount();
|
||||
+ if (remainingItemCount != movedItemCount) {
|
||||
+ origItemStack = origItemStack.copy(true);
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+ if (!origItemStack.isEmpty()) {
|
||||
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
||||
+ }
|
||||
+
|
||||
+ ignoreBlockEntityUpdates = true;
|
||||
+ container.setItem(i, origItemStack);
|
||||
+ ignoreBlockEntityUpdates = false;
|
||||
+ container.setChanged();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(originalItemCount);
|
||||
+
|
||||
+ if (level.paperConfig().hopper.cooldownWhenFull) {
|
||||
+ applyCooldown(hopper);
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static ItemStack callPushMoveEvent(Container destination, ItemStack itemStack, HopperBlockEntity hopper) {
|
||||
+ final org.bukkit.inventory.Inventory destinationInventory = getInventory(destination);
|
||||
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(
|
||||
+ hopper.getOwner(false).getInventory(),
|
||||
+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack),
|
||||
+ destinationInventory,
|
||||
+ true
|
||||
+ );
|
||||
+ final boolean result = event.callEvent();
|
||||
+ if (!event.calledGetItem && !event.calledSetItem) {
|
||||
+ skipPushModeEventFire = true;
|
||||
+ }
|
||||
+ if (!result) {
|
||||
+ applyCooldown(hopper);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (event.calledSetItem) {
|
||||
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ } else {
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
|
||||
+ final org.bukkit.inventory.Inventory sourceInventory = getInventory(container);
|
||||
+ final org.bukkit.inventory.Inventory destination = getInventory(hopper);
|
||||
+
|
||||
+ // Mirror is safe as no plugins ever use this item
|
||||
+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), destination, false);
|
||||
+ final boolean result = event.callEvent();
|
||||
+ if (!event.calledGetItem && !event.calledSetItem) {
|
||||
+ skipPullModeEventFire = true;
|
||||
+ }
|
||||
+ if (!result) {
|
||||
+ applyCooldown(hopper);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (event.calledSetItem) {
|
||||
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
|
||||
+ } else {
|
||||
+ return itemstack;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static org.bukkit.inventory.Inventory getInventory(final Container container) {
|
||||
+ final org.bukkit.inventory.Inventory sourceInventory;
|
||||
+ if (container instanceof net.minecraft.world.CompoundContainer compoundContainer) {
|
||||
+ // Have to special-case large chests as they work oddly
|
||||
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
+ } else if (container instanceof BlockEntity blockEntity) {
|
||||
+ sourceInventory = blockEntity.getOwner(false).getInventory();
|
||||
+ } else if (container.getOwner() != null) {
|
||||
+ sourceInventory = container.getOwner().getInventory();
|
||||
+ } else {
|
||||
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
|
||||
+ }
|
||||
+ return sourceInventory;
|
||||
+ }
|
||||
+
|
||||
+ private static void applyCooldown(final Hopper hopper) {
|
||||
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
|
||||
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean allMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
||||
+ if (container instanceof WorldlyContainer) {
|
||||
+ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) {
|
||||
+ if (!test.test(container.getItem(slot), slot)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ int size = container.getContainerSize();
|
||||
+ for (int slot = 0; slot < size; slot++) {
|
||||
+ if (!test.test(container.getItem(slot), slot)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private static boolean anyMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
||||
+ if (container instanceof WorldlyContainer) {
|
||||
+ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) {
|
||||
+ if (test.test(container.getItem(slot), slot)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ int size = container.getContainerSize();
|
||||
+ for (int slot = 0; slot < size; slot++) {
|
||||
+ if (test.test(container.getItem(slot), slot)) {
|
||||
+ 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 - Perf: Optimize Hoppers
|
||||
+
|
||||
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
|
||||
Container attachedContainer = getAttachedContainer(level, pos, blockEntity);
|
||||
if (attachedContainer == null) {
|
||||
@@ -183,57 +421,60 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
if (isFullContainer(attachedContainer, opposite)) {
|
||||
return false;
|
||||
} else {
|
||||
- for (int i = 0; i < blockEntity.getContainerSize(); i++) {
|
||||
- ItemStack item = blockEntity.getItem(i);
|
||||
- if (!item.isEmpty()) {
|
||||
- int count = item.getCount();
|
||||
- // CraftBukkit start - Call event when pushing items into other inventories
|
||||
- ItemStack original = item.copy();
|
||||
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
|
||||
- blockEntity.removeItem(i, level.spigotConfig.hopperAmount)
|
||||
- ); // Spigot
|
||||
-
|
||||
- org.bukkit.inventory.Inventory destinationInventory;
|
||||
- // Have to special case large chests as they work oddly
|
||||
- if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
|
||||
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
- } else if (attachedContainer.getOwner() != null) {
|
||||
- destinationInventory = attachedContainer.getOwner().getInventory();
|
||||
- } else {
|
||||
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer);
|
||||
- }
|
||||
-
|
||||
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
|
||||
- blockEntity.getOwner().getInventory(),
|
||||
- oitemstack,
|
||||
- destinationInventory,
|
||||
- true
|
||||
- );
|
||||
- if (!event.callEvent()) {
|
||||
- blockEntity.setItem(i, original);
|
||||
- blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
|
||||
- return false;
|
||||
- }
|
||||
- int origCount = event.getItem().getAmount(); // Spigot
|
||||
- ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite);
|
||||
- // CraftBukkit end
|
||||
-
|
||||
- if (itemStack.isEmpty()) {
|
||||
- attachedContainer.setChanged();
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- item.setCount(count);
|
||||
- // Spigot start
|
||||
- item.shrink(origCount - itemStack.getCount());
|
||||
- if (count <= level.spigotConfig.hopperAmount) {
|
||||
- // Spigot end
|
||||
- blockEntity.setItem(i, item);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return hopperPush(level, attachedContainer, opposite, blockEntity);
|
||||
+ //for (int i = 0; i < blockEntity.getContainerSize(); i++) {
|
||||
+ // ItemStack item = blockEntity.getItem(i);
|
||||
+ // if (!item.isEmpty()) {
|
||||
+ // int count = item.getCount();
|
||||
+ // // CraftBukkit start - Call event when pushing items into other inventories
|
||||
+ // ItemStack original = item.copy();
|
||||
+ // org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
|
||||
+ // blockEntity.removeItem(i, level.spigotConfig.hopperAmount)
|
||||
+ // ); // Spigot
|
||||
+
|
||||
+ // org.bukkit.inventory.Inventory destinationInventory;
|
||||
+ // // Have to special case large chests as they work oddly
|
||||
+ // if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
|
||||
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
+ // } else if (attachedContainer.getOwner() != null) {
|
||||
+ // destinationInventory = attachedContainer.getOwner().getInventory();
|
||||
+ // } else {
|
||||
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer);
|
||||
+ // }
|
||||
+
|
||||
+ // org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
|
||||
+ // blockEntity.getOwner().getInventory(),
|
||||
+ // oitemstack,
|
||||
+ // destinationInventory,
|
||||
+ // true
|
||||
+ // );
|
||||
+ // if (!event.callEvent()) {
|
||||
+ // blockEntity.setItem(i, original);
|
||||
+ // blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot
|
||||
+ // return false;
|
||||
+ // }
|
||||
+ // int origCount = event.getItem().getAmount(); // Spigot
|
||||
+ // ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite);
|
||||
+ // // CraftBukkit end
|
||||
+
|
||||
+ // if (itemStack.isEmpty()) {
|
||||
+ // attachedContainer.setChanged();
|
||||
+ // return true;
|
||||
+ // }
|
||||
+
|
||||
+ // item.setCount(count);
|
||||
+ // // Spigot start
|
||||
+ // item.shrink(origCount - itemStack.getCount());
|
||||
+ // if (count <= level.spigotConfig.hopperAmount) {
|
||||
+ // // Spigot end
|
||||
+ // blockEntity.setItem(i, item);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
+
|
||||
+ //return false;
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +529,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState);
|
||||
if (sourceContainer != null) {
|
||||
Direction direction = Direction.DOWN;
|
||||
+ skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers
|
||||
|
||||
for (int i : getSlots(sourceContainer, direction)) {
|
||||
if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot
|
||||
@@ -313,55 +555,58 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
private static boolean tryTakeInItemFromSlot(Hopper hopper, Container container, int slot, Direction direction, Level level) { // Spigot
|
||||
ItemStack item = container.getItem(slot);
|
||||
if (!item.isEmpty() && canTakeItemFromContainer(hopper, container, item, slot, direction)) {
|
||||
- int count = item.getCount();
|
||||
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
|
||||
- ItemStack original = item.copy();
|
||||
- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
|
||||
- container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
|
||||
- );
|
||||
-
|
||||
- org.bukkit.inventory.Inventory sourceInventory;
|
||||
- // Have to special case large chests as they work oddly
|
||||
- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
|
||||
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
- } else if (container.getOwner() != null) {
|
||||
- sourceInventory = container.getOwner().getInventory();
|
||||
- } else {
|
||||
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
|
||||
- }
|
||||
-
|
||||
- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
|
||||
- sourceInventory,
|
||||
- oitemstack,
|
||||
- hopper.getOwner().getInventory(),
|
||||
- false
|
||||
- );
|
||||
-
|
||||
- if (!event.callEvent()) {
|
||||
- container.setItem(slot, original);
|
||||
-
|
||||
- if (hopper instanceof final HopperBlockEntity hopperBlockEntity) {
|
||||
- hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
- }
|
||||
- int origCount = event.getItem().getAmount(); // Spigot
|
||||
- ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null);
|
||||
- // CraftBukkit end
|
||||
-
|
||||
- if (itemStack.isEmpty()) {
|
||||
- container.setChanged();
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- item.setCount(count);
|
||||
- // Spigot start
|
||||
- item.shrink(origCount - itemStack.getCount());
|
||||
- if (count <= level.spigotConfig.hopperAmount) {
|
||||
- // Spigot end
|
||||
- container.setItem(slot, item);
|
||||
- }
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return hopperPull(level, hopper, container, item, slot);
|
||||
+ //int count = item.getCount();
|
||||
+ //// CraftBukkit start - Call event on collection of items from inventories into the hopper
|
||||
+ //ItemStack original = item.copy();
|
||||
+ //org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(
|
||||
+ // container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot
|
||||
+ //);
|
||||
+
|
||||
+ //org.bukkit.inventory.Inventory sourceInventory;
|
||||
+ //// Have to special case large chests as they work oddly
|
||||
+ //if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) {
|
||||
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
||||
+ //} else if (container.getOwner() != null) {
|
||||
+ // sourceInventory = container.getOwner().getInventory();
|
||||
+ //} else {
|
||||
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container);
|
||||
+ //}
|
||||
+
|
||||
+ //org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent(
|
||||
+ // sourceInventory,
|
||||
+ // oitemstack,
|
||||
+ // hopper.getOwner().getInventory(),
|
||||
+ // false
|
||||
+ //);
|
||||
+
|
||||
+ //if (!event.callEvent()) {
|
||||
+ // container.setItem(slot, original);
|
||||
+
|
||||
+ // if (hopper instanceof final HopperBlockEntity hopperBlockEntity) {
|
||||
+ // hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
|
||||
+ // }
|
||||
+
|
||||
+ // return false;
|
||||
+ //}
|
||||
+ //int origCount = event.getItem().getAmount(); // Spigot
|
||||
+ //ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null);
|
||||
+ //// CraftBukkit end
|
||||
+
|
||||
+ //if (itemStack.isEmpty()) {
|
||||
+ // container.setChanged();
|
||||
+ // return true;
|
||||
+ //}
|
||||
+
|
||||
+ //item.setCount(count);
|
||||
+ //// Spigot start
|
||||
+ //item.shrink(origCount - itemStack.getCount());
|
||||
+ //if (count <= level.spigotConfig.hopperAmount) {
|
||||
+ // // Spigot end
|
||||
+ // container.setItem(slot, item);
|
||||
+ //}
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -370,13 +615,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
public static boolean addItem(Container container, ItemEntity item) {
|
||||
boolean flag = false;
|
||||
// CraftBukkit start
|
||||
+ if (org.bukkit.event.inventory.InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
|
||||
org.bukkit.event.inventory.InventoryPickupItemEvent event = new org.bukkit.event.inventory.InventoryPickupItemEvent(
|
||||
- container.getOwner().getInventory(), (org.bukkit.entity.Item) item.getBukkitEntity()
|
||||
+ getInventory(container), (org.bukkit.entity.Item) item.getBukkitEntity() // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation
|
||||
);
|
||||
if (!event.callEvent()) {
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ } // Paper - Perf: Optimize Hoppers
|
||||
ItemStack itemStack = item.getItem().copy();
|
||||
ItemStack itemStack1 = addItem(null, container, itemStack, null);
|
||||
if (itemStack1.isEmpty()) {
|
||||
@@ -431,7 +678,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
stack = stack.split(destination.getMaxStackSize());
|
||||
}
|
||||
// Spigot end
|
||||
+ ignoreBlockEntityUpdates = true; // Paper - Perf: Optimize Hoppers
|
||||
destination.setItem(slot, stack);
|
||||
+ ignoreBlockEntityUpdates = false; // Paper - Perf: Optimize Hoppers
|
||||
stack = leftover; // Paper - Make hoppers respect inventory max stack size
|
||||
flag = true;
|
||||
} else if (canMergeItems(item, stack)) {
|
||||
@@ -519,13 +768,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Nullable
|
||||
public static Container getContainerAt(Level level, BlockPos pos) {
|
||||
- return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5);
|
||||
+ return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, true); // Paper - Optimize hoppers
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z) {
|
||||
+ // Paper start - Perf: Optimize Hoppers
|
||||
+ return HopperBlockEntity.getContainerAt(level, pos, state, x, y, z, false);
|
||||
+ }
|
||||
+ @Nullable
|
||||
+ private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z, final boolean optimizeEntities) {
|
||||
+ // Paper end - Perf: Optimize Hoppers
|
||||
Container blockContainer = getBlockContainer(level, pos, state);
|
||||
- if (blockContainer == null) {
|
||||
+ if (blockContainer == null && (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers
|
||||
blockContainer = getEntityContainer(level, x, y, z);
|
||||
}
|
||||
|
||||
@@ -551,14 +806,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Nullable
|
||||
private static Container getEntityContainer(Level level, double x, double y, double z) {
|
||||
- List<Entity> entities = level.getEntities(
|
||||
- (Entity)null, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR
|
||||
+ List<Entity> entities = level.getEntitiesOfClass(
|
||||
+ (Class) Container.class, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR // Paper - Perf: Optimize hoppers
|
||||
);
|
||||
return !entities.isEmpty() ? (Container)entities.get(level.random.nextInt(entities.size())) : null;
|
||||
}
|
||||
|
||||
private static boolean canMergeItems(ItemStack stack1, ItemStack stack2) {
|
||||
- return stack1.getCount() <= stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2);
|
||||
+ return stack1.getCount() < stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?!
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
index 73b3ddb120d6b6f89e478960e78bed415baea205..f9c31da81d84033abfc1179fc643bceffe35da17 100644
|
||||
--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
@@ -53,7 +53,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
- this.unpackLootTable(null);
|
||||
+ if (index == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers
|
||||
return super.getItem(index);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 4 Jun 2020 02:24:49 -0400
|
||||
Subject: [PATCH] Optimize Bit Operations by inlining
|
||||
|
||||
Inline bit operations and reduce instruction count to make these hot
|
||||
operations faster
|
||||
|
||||
diff --git a/net/minecraft/core/BlockPos.java b/net/minecraft/core/BlockPos.java
|
||||
index 98f0b1cf19d7a035849a9a2fa25e2be3a4c5a980..0822a0faf7af9e746e7936ac17597b1f51e40a92 100644
|
||||
--- a/net/minecraft/core/BlockPos.java
|
||||
+++ b/net/minecraft/core/BlockPos.java
|
||||
@@ -51,15 +51,17 @@ public class BlockPos extends Vec3i {
|
||||
};
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final BlockPos ZERO = new BlockPos(0, 0, 0);
|
||||
- public static final int PACKED_HORIZONTAL_LENGTH = 1 + Mth.log2(Mth.smallestEncompassingPowerOfTwo(30000000));
|
||||
- public static final int PACKED_Y_LENGTH = 64 - 2 * PACKED_HORIZONTAL_LENGTH;
|
||||
- private static final long PACKED_X_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
|
||||
- private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L;
|
||||
- private static final long PACKED_Z_MASK = (1L << PACKED_HORIZONTAL_LENGTH) - 1L;
|
||||
+ // Paper start - Optimize Bit Operations by inlining
|
||||
+ public static final int PACKED_HORIZONTAL_LENGTH = 26;
|
||||
+ public static final int PACKED_Y_LENGTH = 12;
|
||||
+ private static final long PACKED_X_MASK = 67108863;
|
||||
+ private static final long PACKED_Y_MASK = 4095;
|
||||
+ private static final long PACKED_Z_MASK = 67108863;
|
||||
private static final int Y_OFFSET = 0;
|
||||
- private static final int Z_OFFSET = PACKED_Y_LENGTH;
|
||||
- private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_HORIZONTAL_LENGTH;
|
||||
- public static final int MAX_HORIZONTAL_COORDINATE = (1 << PACKED_HORIZONTAL_LENGTH) / 2 - 1;
|
||||
+ private static final int Z_OFFSET = 12;
|
||||
+ private static final int X_OFFSET = 38;
|
||||
+ public static final int MAX_HORIZONTAL_COORDINATE = 33554431;
|
||||
+ // Paper end - Optimize Bit Operations by inlining
|
||||
|
||||
public BlockPos(int x, int y, int z) {
|
||||
super(x, y, z);
|
||||
@@ -69,28 +71,29 @@ public class BlockPos extends Vec3i {
|
||||
this(vector.getX(), vector.getY(), vector.getZ());
|
||||
}
|
||||
|
||||
+ public static long getAdjacent(int baseX, int baseY, int baseZ, Direction direction) { return asLong(baseX + direction.getStepX(), baseY + direction.getStepY(), baseZ + direction.getStepZ()); } // Paper
|
||||
public static long offset(long pos, Direction direction) {
|
||||
return offset(pos, direction.getStepX(), direction.getStepY(), direction.getStepZ());
|
||||
}
|
||||
|
||||
public static long offset(long pos, int dx, int dy, int dz) {
|
||||
- return asLong(getX(pos) + dx, getY(pos) + dy, getZ(pos) + dz);
|
||||
+ return asLong((int) (pos >> 38) + dx, (int) ((pos << 52) >> 52) + dy, (int) ((pos << 26) >> 38) + dz); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static int getX(long packedPos) {
|
||||
- return (int)(packedPos << 64 - X_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH);
|
||||
+ return (int) (packedPos >> 38); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static int getY(long packedPos) {
|
||||
- return (int)(packedPos << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH);
|
||||
+ return (int) ((packedPos << 52) >> 52); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static int getZ(long packedPos) {
|
||||
- return (int)(packedPos << 64 - Z_OFFSET - PACKED_HORIZONTAL_LENGTH >> 64 - PACKED_HORIZONTAL_LENGTH);
|
||||
+ return (int) ((packedPos << 26) >> 38); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static BlockPos of(long packedPos) {
|
||||
- return new BlockPos(getX(packedPos), getY(packedPos), getZ(packedPos));
|
||||
+ return new BlockPos((int) (packedPos >> 38), (int) ((packedPos << 52) >> 52), (int) ((packedPos << 26) >> 38)); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static BlockPos containing(double x, double y, double z) {
|
||||
@@ -114,10 +117,7 @@ public class BlockPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static long asLong(int x, int y, int z) {
|
||||
- long l = 0L;
|
||||
- l |= (x & PACKED_X_MASK) << X_OFFSET;
|
||||
- l |= (y & PACKED_Y_MASK) << 0;
|
||||
- return l | (z & PACKED_Z_MASK) << Z_OFFSET;
|
||||
+ return ((x & 67108863L) << 38) | ((y & 4095L)) | ((z & 67108863L) << 12); // Paper - inline constants and simplify
|
||||
}
|
||||
|
||||
public static long getFlatIndex(long packedPos) {
|
||||
diff --git a/net/minecraft/core/SectionPos.java b/net/minecraft/core/SectionPos.java
|
||||
index 1780d8e14cea62971da75e4dcc80d1805434037b..ce8c394ea1a7bc5bf5c568c82e6158b19df517d8 100644
|
||||
--- a/net/minecraft/core/SectionPos.java
|
||||
+++ b/net/minecraft/core/SectionPos.java
|
||||
@@ -38,7 +38,7 @@ public class SectionPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static SectionPos of(BlockPos pos) {
|
||||
- return new SectionPos(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ()));
|
||||
+ return new SectionPos(pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4); // Paper
|
||||
}
|
||||
|
||||
public static SectionPos of(ChunkPos chunkPos, int y) {
|
||||
@@ -54,7 +54,7 @@ public class SectionPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static SectionPos of(long packed) {
|
||||
- return new SectionPos(x(packed), y(packed), z(packed));
|
||||
+ return new SectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); // Paper
|
||||
}
|
||||
|
||||
public static SectionPos bottomOf(ChunkAccess chunk) {
|
||||
@@ -65,8 +65,16 @@ public class SectionPos extends Vec3i {
|
||||
return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ());
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public static long getAdjacentFromBlockPos(int x, int y, int z, Direction direction) {
|
||||
+ return (((long) ((x >> 4) + direction.getStepX()) & 4194303L) << 42) | (((long) ((y >> 4) + direction.getStepY()) & 1048575L)) | (((long) ((z >> 4) + direction.getStepZ()) & 4194303L) << 20);
|
||||
+ }
|
||||
+ public static long getAdjacentFromSectionPos(int x, int y, int z, Direction direction) {
|
||||
+ return (((long) (x + direction.getStepX()) & 4194303L) << 42) | (((long) ((y) + direction.getStepY()) & 1048575L)) | (((long) (z + direction.getStepZ()) & 4194303L) << 20);
|
||||
+ }
|
||||
+ // Paper end
|
||||
public static long offset(long packed, int dx, int dy, int dz) {
|
||||
- return asLong(x(packed) + dx, y(packed) + dy, z(packed) + dz);
|
||||
+ return (((long) ((int) (packed >> 42) + dx) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + dy) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + dz) & 4194303L) << 20); // Simplify to reduce instruction count
|
||||
}
|
||||
|
||||
public static int posToSectionCoord(double pos) {
|
||||
@@ -86,10 +94,7 @@ public class SectionPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static short sectionRelativePos(BlockPos pos) {
|
||||
- int relativeBlockPosX = sectionRelative(pos.getX());
|
||||
- int relativeBlockPosY = sectionRelative(pos.getY());
|
||||
- int relativeBlockPosZ = sectionRelative(pos.getZ());
|
||||
- return (short)(relativeBlockPosX << 8 | relativeBlockPosZ << 4 | relativeBlockPosY << 0);
|
||||
+ return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static int sectionRelativeX(short x) {
|
||||
@@ -152,16 +157,16 @@ public class SectionPos extends Vec3i {
|
||||
return this.getZ();
|
||||
}
|
||||
|
||||
- public int minBlockX() {
|
||||
- return sectionToBlockCoord(this.x());
|
||||
+ public final int minBlockX() { // Paper - final
|
||||
+ return this.getX() << 4; // Paper - inline
|
||||
}
|
||||
|
||||
- public int minBlockY() {
|
||||
- return sectionToBlockCoord(this.y());
|
||||
+ public final int minBlockY() { // Paper - final
|
||||
+ return this.getY() << 4; // Paper - inline
|
||||
}
|
||||
|
||||
- public int minBlockZ() {
|
||||
- return sectionToBlockCoord(this.z());
|
||||
+ public final int minBlockZ() { // Paper - final
|
||||
+ return this.getZ() << 4; // Paper - inline
|
||||
}
|
||||
|
||||
public int maxBlockX() {
|
||||
@@ -177,7 +182,7 @@ public class SectionPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static long blockToSection(long levelPos) {
|
||||
- return asLong(blockToSectionCoord(BlockPos.getX(levelPos)), blockToSectionCoord(BlockPos.getY(levelPos)), blockToSectionCoord(BlockPos.getZ(levelPos)));
|
||||
+ return (((long) (int) (levelPos >> 42) & 4194303L) << 42) | (((long) (int) ((levelPos << 52) >> 56) & 1048575L)) | (((long) (int) ((levelPos << 26) >> 42) & 4194303L) << 20); // Simplify to reduce instruction count
|
||||
}
|
||||
|
||||
public static long getZeroNode(int x, int z) {
|
||||
@@ -205,15 +210,17 @@ public class SectionPos extends Vec3i {
|
||||
return asLong(blockToSectionCoord(blockPos.getX()), blockToSectionCoord(blockPos.getY()), blockToSectionCoord(blockPos.getZ()));
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public static long blockPosAsSectionLong(int x, int y, int z) {
|
||||
+ return (((long) (x >> 4) & 4194303L) << 42) | (((long) (y >> 4) & 1048575L)) | (((long) (z >> 4) & 4194303L) << 20);
|
||||
+ }
|
||||
+ // Paper end
|
||||
public static long asLong(int x, int y, int z) {
|
||||
- long l = 0L;
|
||||
- l |= (x & 4194303L) << 42;
|
||||
- l |= (y & 1048575L) << 0;
|
||||
- return l | (z & 4194303L) << 20;
|
||||
+ return ((x & 4194303L) << 42) | ((y & 1048575L)) | ((z & 4194303L) << 20); // Paper - Simplify to reduce instruction count
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
- return asLong(this.x(), this.y(), this.z());
|
||||
+ return ((this.getX() & 4194303L) << 42) | ((this.getY() & 1048575L)) | ((this.getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,16 +233,13 @@ public class SectionPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static Stream<SectionPos> cube(SectionPos center, int radius) {
|
||||
- int sectionX = center.x();
|
||||
- int sectionY = center.y();
|
||||
- int sectionZ = center.z();
|
||||
- return betweenClosedStream(sectionX - radius, sectionY - radius, sectionZ - radius, sectionX + radius, sectionY + radius, sectionZ + radius);
|
||||
+ return betweenClosedStream(center.getX() - radius, center.getY() - radius, center.getZ() - radius, center.getX() + radius, center.getY() + radius, center.getZ() + radius); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
- public static Stream<SectionPos> aroundChunk(ChunkPos chunkPos, int x, int y, int z) {
|
||||
+ public static Stream<SectionPos> aroundChunk(ChunkPos chunkPos, int radius, int minY, int maxY) { // Paper - fix params
|
||||
int i = chunkPos.x;
|
||||
int i1 = chunkPos.z;
|
||||
- return betweenClosedStream(i - x, y, i1 - x, i + x, z, i1 + x);
|
||||
+ return betweenClosedStream(i - radius, minY, i1 - radius, i + radius, maxY, i1 + radius); // Paper - simplify/inline
|
||||
}
|
||||
|
||||
public static Stream<SectionPos> betweenClosedStream(final int x1, final int y1, final int z1, final int x2, final int y2, final int z2) {
|
||||
@@ -0,0 +1,223 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Josh Roy <10731363+JRoy@users.noreply.github.com>
|
||||
Date: Wed, 1 Jul 2020 18:01:49 -0400
|
||||
Subject: [PATCH] Remove streams from hot code
|
||||
|
||||
Co-authored-by: Bjarne Koll <git@lynxplay.dev>
|
||||
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
|
||||
diff --git a/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
index c215d97c24e6501e1a48a76fc08bf48ff4dfe462..bd31d1cac0d022a72bd536c41d1ef811886e7068 100644
|
||||
--- a/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
+++ b/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
||||
@@ -57,7 +57,7 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
if (this.hasRequiredMemories(entity)) {
|
||||
this.status = Behavior.Status.RUNNING;
|
||||
this.orderPolicy.apply(this.behaviors);
|
||||
- this.runningPolicy.apply(this.behaviors.stream(), level, entity, gameTime);
|
||||
+ this.runningPolicy.apply(this.behaviors, level, entity, gameTime); // Paper - Perf: Remove streams from hot code
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -66,10 +66,13 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
|
||||
@Override
|
||||
public final void tickOrStop(ServerLevel level, E entity, long gameTime) {
|
||||
- this.behaviors
|
||||
- .stream()
|
||||
- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)
|
||||
- .forEach(behavior -> behavior.tickOrStop(level, entity, gameTime));
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
+ for (final BehaviorControl<? super E> behavior : this.behaviors) {
|
||||
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
|
||||
+ behavior.tickOrStop(level, entity, gameTime);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
if (this.behaviors.stream().noneMatch(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)) {
|
||||
this.doStop(level, entity, gameTime);
|
||||
}
|
||||
@@ -78,11 +81,16 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
@Override
|
||||
public final void doStop(ServerLevel level, E entity, long gameTime) {
|
||||
this.status = Behavior.Status.STOPPED;
|
||||
- this.behaviors
|
||||
- .stream()
|
||||
- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)
|
||||
- .forEach(behavior -> behavior.doStop(level, entity, gameTime));
|
||||
- this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
+ for (final BehaviorControl<? super E> behavior : this.behaviors) {
|
||||
+ if (behavior.getStatus() == Behavior.Status.RUNNING) {
|
||||
+ behavior.doStop(level, entity, gameTime);
|
||||
+ }
|
||||
+ }
|
||||
+ for (final MemoryModuleType<?> exitErasedMemory : this.exitErasedMemories) {
|
||||
+ entity.getBrain().eraseMemory(exitErasedMemory);
|
||||
+ }
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,20 +124,30 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
||||
|
||||
public static enum RunningPolicy {
|
||||
RUN_ONE {
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
@Override
|
||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
|
||||
- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED)
|
||||
- .filter(behavior -> behavior.tryStart(level, owner, gameTime))
|
||||
- .findFirst();
|
||||
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
|
||||
+ for (final BehaviorControl<? super E> behavior : behaviors) {
|
||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(level, owner, gameTime)) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
}
|
||||
},
|
||||
TRY_ALL {
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
@Override
|
||||
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
|
||||
- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED).forEach(behavior -> behavior.tryStart(level, owner, gameTime));
|
||||
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) {
|
||||
+ for (final BehaviorControl<? super E> behavior : behaviors) {
|
||||
+ if (behavior.getStatus() == Behavior.Status.STOPPED) {
|
||||
+ behavior.tryStart(level, owner, gameTime);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
}
|
||||
};
|
||||
|
||||
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime);
|
||||
+ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime); // Paper - Perf: Remove streams from hot code
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
index 2c839dc80f451c83135828a97aced1a531004bab..b74a4ce1b629d440681a1f5c026997ccaf1d0373 100644
|
||||
--- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
+++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
@@ -59,8 +59,22 @@ public class GossipContainer {
|
||||
return this.gossips.entrySet().stream().flatMap(gossip -> gossip.getValue().unpack(gossip.getKey()));
|
||||
}
|
||||
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
+ private List<GossipContainer.GossipEntry> decompress() {
|
||||
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
||||
+ for (Map.Entry<UUID, GossipContainer.EntityGossips> entry : this.gossips.entrySet()) {
|
||||
+ for (GossipContainer.GossipEntry cur : entry.getValue().decompress(entry.getKey())) {
|
||||
+ if (cur.weightedValue() != 0) {
|
||||
+ list.add(cur);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return list;
|
||||
+ }
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
+
|
||||
private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int amount) {
|
||||
- List<GossipContainer.GossipEntry> list = this.unpack().toList();
|
||||
+ List<GossipContainer.GossipEntry> list = this.decompress(); // Paper - Perf: Remove streams from hot code
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
@@ -145,7 +159,7 @@ public class GossipContainer {
|
||||
|
||||
public <T> T store(DynamicOps<T> ops) {
|
||||
return GossipContainer.GossipEntry.LIST_CODEC
|
||||
- .encodeStart(ops, this.unpack().toList())
|
||||
+ .encodeStart(ops, this.decompress()) // Paper - Perf: Remove streams from hot code
|
||||
.resultOrPartial(errorMessage -> LOGGER.warn("Failed to serialize gossips: {}", errorMessage))
|
||||
.orElseGet(ops::emptyList);
|
||||
}
|
||||
@@ -172,12 +186,23 @@ public class GossipContainer {
|
||||
final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>();
|
||||
|
||||
public int weightedValue(Predicate<GossipType> gossipType) {
|
||||
- return this.entries
|
||||
- .object2IntEntrySet()
|
||||
- .stream()
|
||||
- .filter(gossip -> gossipType.test(gossip.getKey()))
|
||||
- .mapToInt(gossip -> gossip.getIntValue() * gossip.getKey().weight)
|
||||
- .sum();
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
+ int weight = 0;
|
||||
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
|
||||
+ if (gossipType.test(entry.getKey())) {
|
||||
+ weight += entry.getIntValue() * entry.getKey().weight;
|
||||
+ }
|
||||
+ }
|
||||
+ return weight;
|
||||
+ }
|
||||
+
|
||||
+ public List<GossipContainer.GossipEntry> decompress(UUID uuid) {
|
||||
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
||||
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
|
||||
+ list.add(new GossipContainer.GossipEntry(uuid, entry.getKey(), entry.getIntValue()));
|
||||
+ }
|
||||
+ return list;
|
||||
+ // Paper end - Perf: Remove streams from hot code
|
||||
}
|
||||
|
||||
public Stream<GossipContainer.GossipEntry> unpack(UUID identifier) {
|
||||
diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
index 38873e56e95dc772b184e4271f7af1fb411ac9f8..09fd13e2d958da8326276c4dadf25bf488aff5ac 100644
|
||||
--- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
+++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
||||
@@ -24,13 +24,17 @@ public class NearestItemSensor extends Sensor<Mob> {
|
||||
@Override
|
||||
protected void doTick(ServerLevel level, Mob entity) {
|
||||
Brain<?> brain = entity.getBrain();
|
||||
- List<ItemEntity> entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true);
|
||||
+ List<ItemEntity> entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities
|
||||
entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr));
|
||||
- Optional<ItemEntity> optional = entitiesOfClass.stream()
|
||||
- .filter(itemEntity -> entity.wantsToPickUp(level, itemEntity.getItem()))
|
||||
- .filter(itemEntity -> itemEntity.closerThan(entity, 32.0))
|
||||
- .filter(entity::hasLineOfSight)
|
||||
- .findFirst();
|
||||
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional);
|
||||
+ // Paper start - Perf: remove streams from hot code
|
||||
+ ItemEntity nearest = null;
|
||||
+ for (final ItemEntity itemEntity : entitiesOfClass) {
|
||||
+ if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities
|
||||
+ nearest = itemEntity;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
|
||||
+ // Paper end - Perf: remove streams from hot code
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java
|
||||
index 1a09da5aa1ae047a002d6779326c2a29e47d32b5..131923282c9ecbcb1d7f45a826da907c02bd2716 100644
|
||||
--- a/net/minecraft/world/level/levelgen/Beardifier.java
|
||||
+++ b/net/minecraft/world/level/levelgen/Beardifier.java
|
||||
@@ -35,9 +35,10 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
|
||||
int minBlockZ = chunkPos.getMinBlockZ();
|
||||
ObjectList<Beardifier.Rigid> list = new ObjectArrayList<>(10);
|
||||
ObjectList<JigsawJunction> list1 = new ObjectArrayList<>(32);
|
||||
- structureManager.startsForStructure(chunkPos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE)
|
||||
- .forEach(
|
||||
- structureStart -> {
|
||||
+ // Paper start - Perf: Remove streams from hot code
|
||||
+ for (net.minecraft.world.level.levelgen.structure.StructureStart structureStart : structureManager.startsForStructure(chunkPos, structure -> {
|
||||
+ return structure.terrainAdaptation() != TerrainAdjustment.NONE;
|
||||
+ })) { // Paper end - Perf: Remove streams from hot code
|
||||
TerrainAdjustment terrainAdjustment = structureStart.getStructure().terrainAdaptation();
|
||||
|
||||
for (StructurePiece structurePiece : structureStart.getPieces()) {
|
||||
@@ -65,8 +66,7 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
|
||||
}
|
||||
}
|
||||
}
|
||||
- }
|
||||
- );
|
||||
+ } // Paper - Perf: Remove streams from hot code
|
||||
return new Beardifier(list.iterator(), list1.iterator());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Tue, 4 Aug 2020 22:24:15 +0200
|
||||
Subject: [PATCH] Optimize Pathfinder - Remove Streams / Optimized collections
|
||||
|
||||
I utilized the IDE to convert streams to non streams code, so shouldn't
|
||||
be any risk of behavior change. Only did minor optimization of the
|
||||
generated code set to remove unnecessary things.
|
||||
|
||||
I expect us to just drop this patch on next major update and re-apply
|
||||
it with the IDE again and re-apply the collections optimization.
|
||||
|
||||
Optimize collection by creating a list instead of a set of the key and value.
|
||||
|
||||
This lets us get faster foreach iteration, as well as avoids map lookups on
|
||||
the values when needed.
|
||||
|
||||
diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
index a6ef296f7af1f784e1f0772947a6cb3519a3bc2a..81de6c1bbef1cafd3036e736dd305fbedc8368c6 100644
|
||||
--- a/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
+++ b/net/minecraft/world/level/pathfinder/PathFinder.java
|
||||
@@ -43,8 +43,12 @@ public class PathFinder {
|
||||
if (start == null) {
|
||||
return null;
|
||||
} else {
|
||||
- Map<Target, BlockPos> map = targetPositions.stream()
|
||||
- .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), Function.identity()));
|
||||
+ // Paper start - Perf: remove streams and optimize collection
|
||||
+ List<Map.Entry<Target, BlockPos>> map = Lists.newArrayList();
|
||||
+ for (BlockPos pos : targetPositions) {
|
||||
+ map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
|
||||
+ }
|
||||
+ // Paper end - Perf: remove streams and optimize collection
|
||||
Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
|
||||
this.nodeEvaluator.done();
|
||||
return path;
|
||||
@@ -52,19 +56,19 @@ public class PathFinder {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
- private Path findPath(Node node, Map<Target, BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
|
||||
+ private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
|
||||
ProfilerFiller profilerFiller = Profiler.get();
|
||||
profilerFiller.push("find_path");
|
||||
profilerFiller.markForCharting(MetricCategory.PATH_FINDING);
|
||||
- Set<Target> set = targetPositions.keySet();
|
||||
+ // Set<Target> set = targetPositions.keySet(); // Paper
|
||||
node.g = 0.0F;
|
||||
- node.h = this.getBestH(node, set);
|
||||
+ node.h = this.getBestH(node, positions); // Paper - optimize collection
|
||||
node.f = node.h;
|
||||
this.openSet.clear();
|
||||
this.openSet.insert(node);
|
||||
- Set<Node> set1 = ImmutableSet.of();
|
||||
+ // Set<Node> set1 = ImmutableSet.of(); // Paper - unused - diff on change
|
||||
int i = 0;
|
||||
- Set<Target> set2 = Sets.newHashSetWithExpectedSize(set.size());
|
||||
+ List<Map.Entry<Target, BlockPos>> entryList = Lists.newArrayListWithExpectedSize(positions.size()); // Paper - optimize collection
|
||||
int i1 = (int)(this.maxVisitedNodes * searchDepthMultiplier);
|
||||
|
||||
while (!this.openSet.isEmpty()) {
|
||||
@@ -75,14 +79,18 @@ public class PathFinder {
|
||||
Node node1 = this.openSet.pop();
|
||||
node1.closed = true;
|
||||
|
||||
- for (Target target : set) {
|
||||
+ // Paper start - optimize collection
|
||||
+ for (int positionIndex = 0, size = positions.size(); positionIndex < size; positionIndex++) {
|
||||
+ final Map.Entry<Target, BlockPos> entry = positions.get(positionIndex);
|
||||
+ Target target = entry.getKey();
|
||||
if (node1.distanceManhattan(target) <= accuracy) {
|
||||
target.setReached();
|
||||
- set2.add(target);
|
||||
+ entryList.add(entry);
|
||||
+ // Paper end - Perf: remove streams and optimize collection
|
||||
}
|
||||
}
|
||||
|
||||
- if (!set2.isEmpty()) {
|
||||
+ if (!entryList.isEmpty()) { // Paper - Perf: remove streams and optimize collection; rename
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -97,7 +105,7 @@ public class PathFinder {
|
||||
if (node2.walkedDistance < maxRange && (!node2.inOpenSet() || f1 < node2.g)) {
|
||||
node2.cameFrom = node1;
|
||||
node2.g = f1;
|
||||
- node2.h = this.getBestH(node2, set) * 1.5F;
|
||||
+ node2.h = this.getBestH(node2, positions) * 1.5F; // Paper - Perf: remove streams and optimize collection
|
||||
if (node2.inOpenSet()) {
|
||||
this.openSet.changeCost(node2, node2.g + node2.h);
|
||||
} else {
|
||||
@@ -109,25 +117,34 @@ public class PathFinder {
|
||||
}
|
||||
}
|
||||
|
||||
- Optional<Path> optional = !set2.isEmpty()
|
||||
- ? set2.stream()
|
||||
- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), true))
|
||||
- .min(Comparator.comparingInt(Path::getNodeCount))
|
||||
- : set.stream()
|
||||
- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), false))
|
||||
- .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
|
||||
+ // Paper start - Perf: remove streams and optimize collection
|
||||
+ Path best = null;
|
||||
+ boolean entryListIsEmpty = entryList.isEmpty();
|
||||
+ Comparator<Path> comparator = entryListIsEmpty
|
||||
+ ? Comparator.comparingInt(Path::getNodeCount)
|
||||
+ : Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount);
|
||||
+ for (Map.Entry<Target, BlockPos> entry : entryListIsEmpty ? positions : entryList) {
|
||||
+ Path path = this.reconstructPath(entry.getKey().getBestNode(), entry.getValue(), !entryListIsEmpty);
|
||||
+ if (best == null || comparator.compare(path, best) < 0) {
|
||||
+ best = path;
|
||||
+ }
|
||||
+ }
|
||||
profilerFiller.pop();
|
||||
- return optional.isEmpty() ? null : optional.get();
|
||||
+ return best;
|
||||
+ // Paper end - Perf: remove streams and optimize collection
|
||||
}
|
||||
|
||||
protected float distance(Node first, Node second) {
|
||||
return first.distanceTo(second);
|
||||
}
|
||||
|
||||
- private float getBestH(Node node, Set<Target> targets) {
|
||||
+ private float getBestH(Node node, List<Map.Entry<Target, BlockPos>> targets) { // Paper - Perf: remove streams and optimize collection; Set<Target> -> List<Map.Entry<Target, BlockPos>>
|
||||
float f = Float.MAX_VALUE;
|
||||
|
||||
- for (Target target : targets) {
|
||||
+ // Paper start - Perf: remove streams and optimize collection
|
||||
+ for (int i = 0, targetsSize = targets.size(); i < targetsSize; i++) {
|
||||
+ final Target target = targets.get(i).getKey();
|
||||
+ // Paper end - Perf: remove streams and optimize collection
|
||||
float f1 = node.distanceTo(target);
|
||||
target.updateBest(f1, node);
|
||||
f = Math.min(f1, f);
|
||||
@@ -30621,7 +30621,7 @@ index 1110ca4075a1bbaa46b66686435dab91b275c945..c2218630c3074c8b3f82364e37503b12
|
||||
return structureTemplate.save(new CompoundTag());
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index d450d4af96716caff4b29a84d1d83ec4010854f0..646c2f2b617ed706021c83c9fc4492860dfdd4e9 100644
|
||||
index 8657e4fd7c5e0e23b69d9a982408a7d038f0a787..415ab7faf371507e278d0da5bf0ffa9972585876 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -301,6 +301,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
Reference in New Issue
Block a user