mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 19:37:35 +01:00
Re-add Optimize Hoppers patch
This commit is contained in:
parent
4dd6ddb86b
commit
9a279b3947
287
Spigot-Server-Patches/0306-Optimize-Hoppers.patch
Normal file
287
Spigot-Server-Patches/0306-Optimize-Hoppers.patch
Normal file
@ -0,0 +1,287 @@
|
||||
From c48ed623914dfeed35ef536b48b527415a70eabd 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
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
index b9f5f4905..a8470e6e7 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
@@ -399,6 +399,15 @@ public class PaperWorldConfig {
|
||||
squidMaxSpawnHeight = getDouble("squid-spawn-height.maximum", 0.0D);
|
||||
}
|
||||
|
||||
+ 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"));
|
||||
+ }
|
||||
+
|
||||
public boolean disableSprintInterruptionOnAttack;
|
||||
private void disableSprintInterruptionOnAttack() {
|
||||
disableSprintInterruptionOnAttack = getBoolean("game-mechanics.disable-sprint-interruption-on-attack", false);
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index ebc0709eb..d989b065d 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1017,6 +1017,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
|
||||
|
||||
if (true || i == 0 || this.getAllowNether()) { // CraftBukkit
|
||||
WorldServer worldserver = this.worlds.get(i);
|
||||
+ TileEntityHopper.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
|
||||
|
||||
this.methodProfiler.a(() -> {
|
||||
return worldserver.getWorldData().getName();
|
||||
diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java
|
||||
index 3e9b357c8..db78274a8 100644
|
||||
--- a/src/main/java/net/minecraft/server/TileEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/TileEntity.java
|
||||
@@ -49,6 +49,7 @@ public abstract class TileEntity implements KeyedObject {
|
||||
public void setCurrentChunk(Chunk chunk) {
|
||||
this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;
|
||||
}
|
||||
+ static boolean IGNORE_TILE_UPDATES = false;
|
||||
// Paper end
|
||||
|
||||
@Nullable
|
||||
@@ -121,6 +122,7 @@ public abstract class TileEntity implements KeyedObject {
|
||||
|
||||
public void update() {
|
||||
if (this.world != null) {
|
||||
+ if (IGNORE_TILE_UPDATES) return; // Paper
|
||||
this.f = this.world.getType(this.position);
|
||||
this.world.b(this.position, this);
|
||||
if (!this.f.isAir()) {
|
||||
diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java
|
||||
index bb41d4780..9e7a91fe4 100644
|
||||
--- a/src/main/java/net/minecraft/server/TileEntityHopper.java
|
||||
+++ b/src/main/java/net/minecraft/server/TileEntityHopper.java
|
||||
@@ -190,6 +190,154 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
return false;
|
||||
}
|
||||
|
||||
+ // Paper start - Optimize Hoppers
|
||||
+ private static boolean skipPullModeEventFire = false;
|
||||
+ private static boolean skipPushModeEventFire = false;
|
||||
+ static boolean skipHopperEvents = false;
|
||||
+
|
||||
+ private boolean hopperPush(IInventory iinventory, EnumDirection enumdirection) {
|
||||
+ skipPushModeEventFire = skipHopperEvents;
|
||||
+ boolean foundItem = false;
|
||||
+ for (int i = 0; i < this.getSize(); ++i) {
|
||||
+ if (!this.getItem(i).isEmpty()) {
|
||||
+ foundItem = true;
|
||||
+ ItemStack origItemStack = this.getItem(i);
|
||||
+ ItemStack itemstack = origItemStack;
|
||||
+
|
||||
+ final int origCount = origItemStack.getCount();
|
||||
+ final int moved = Math.min(world.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();
|
||||
+ origItemStack.setCount(origCount - moved + remaining);
|
||||
+ this.setItem(i, origItemStack);
|
||||
+ iinventory.update();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(origCount);
|
||||
+ }
|
||||
+ }
|
||||
+ if (foundItem && world.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown
|
||||
+ this.setCooldown(world.spigotConfig.hopperTransfer);
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private static boolean hopperPull(IHopper ihopper, IInventory iinventory, int i) {
|
||||
+ ItemStack origItemStack = iinventory.getItem(i);
|
||||
+ ItemStack itemstack = origItemStack;
|
||||
+ final int origCount = origItemStack.getCount();
|
||||
+ final World world = ihopper.getWorld();
|
||||
+ 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();
|
||||
+ origItemStack.setCount(origCount - moved + remaining);
|
||||
+ IGNORE_TILE_UPDATES = true;
|
||||
+ iinventory.setItem(i, origItemStack);
|
||||
+ IGNORE_TILE_UPDATES = false;
|
||||
+ iinventory.update();
|
||||
+ return true;
|
||||
+ }
|
||||
+ origItemStack.setCount(origCount);
|
||||
+
|
||||
+ if (world.paperConfig.cooldownHopperWhenFull) {
|
||||
+ cooldownHopper(ihopper);
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private ItemStack callPushMoveEvent(IInventory 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(IHopper hopper, IInventory 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(IInventory iinventory) {
|
||||
+ Inventory sourceInventory;// Have to special case large chests as they work oddly
|
||||
+ if (iinventory instanceof InventoryLargeChest) {
|
||||
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
|
||||
+ } else if (iinventory instanceof TileEntity) {
|
||||
+ sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory();
|
||||
+ } else {
|
||||
+ sourceInventory = iinventory.getOwner().getInventory();
|
||||
+ }
|
||||
+ return sourceInventory;
|
||||
+ }
|
||||
+
|
||||
+ private static void cooldownHopper(IHopper hopper) {
|
||||
+ if (hopper instanceof TileEntityHopper) {
|
||||
+ ((TileEntityHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer);
|
||||
+ } else if (hopper instanceof EntityMinecartHopper) {
|
||||
+ ((EntityMinecartHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer / 2);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Paper end
|
||||
private boolean s() {
|
||||
IInventory iinventory = this.D();
|
||||
|
||||
@@ -201,6 +349,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
if (this.a(iinventory, enumdirection)) {
|
||||
return false;
|
||||
} else {
|
||||
+ 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).cloneItemStack();
|
||||
@@ -242,7 +391,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
}
|
||||
}
|
||||
|
||||
- return false;
|
||||
+ return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,6 +462,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
if (b(iinventory, enumdirection)) {
|
||||
return false;
|
||||
}
|
||||
+ skipPullModeEventFire = skipHopperEvents; // Paper
|
||||
|
||||
if (iinventory instanceof IWorldInventory) {
|
||||
IWorldInventory iworldinventory = (IWorldInventory) iinventory;
|
||||
@@ -355,6 +505,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
ItemStack itemstack = iinventory.getItem(i);
|
||||
|
||||
if (!itemstack.isEmpty() && b(iinventory, itemstack, i, enumdirection)) {
|
||||
+ return hopperPull(ihopper, iinventory, i); /* // 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
|
||||
@@ -395,7 +546,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
}
|
||||
|
||||
itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot
|
||||
- iinventory.setItem(i, itemstack1);
|
||||
+ iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -404,7 +555,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
public static boolean a(IInventory iinventory, EntityItem entityitem) {
|
||||
boolean flag = false;
|
||||
// CraftBukkit start
|
||||
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(iinventory.getOwner().getInventory(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
|
||||
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation
|
||||
entityitem.world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
@@ -458,7 +609,9 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
|
||||
boolean flag1 = iinventory1.P_();
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
+ IGNORE_TILE_UPDATES = true; // Paper
|
||||
iinventory1.setItem(i, itemstack);
|
||||
+ IGNORE_TILE_UPDATES = false; // Paper
|
||||
itemstack = ItemStack.a;
|
||||
flag = true;
|
||||
} else if (a(itemstack1, itemstack)) {
|
||||
--
|
||||
2.18.0
|
||||
|
@ -1,112 +0,0 @@
|
||||
From 9b92490838fd8805b3e867cc7235a0bcf0854f7f Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Mon, 1 Jan 2018 16:10:24 -0500
|
||||
Subject: [PATCH] Configurable Max Chunk Gens per Tick
|
||||
|
||||
Limit the number of generations that can occur in a single tick, forcing them
|
||||
to be spread out more.
|
||||
|
||||
Defaulting to 10 as an average generation is going to be 3-6ms, which means 10 will
|
||||
likely cause the server to lose TPS, but constrain how much.
|
||||
|
||||
This should result in no noticeable speed reduction in generation for servers not
|
||||
lagging, and let larger servers reduce this value according to their own desires.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
index e3ff78180..0ddf826f9 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
@@ -472,4 +472,16 @@ public class PaperWorldConfig {
|
||||
}
|
||||
log("Max Chunk Sends Per Tick: " + maxChunkSendsPerTick);
|
||||
}
|
||||
+
|
||||
+ public int maxChunkGensPerTick = 10;
|
||||
+ private void maxChunkGensPerTick() {
|
||||
+ maxChunkGensPerTick = getInt("max-chunk-gens-per-tick", maxChunkGensPerTick);
|
||||
+ if (maxChunkGensPerTick <= 0) {
|
||||
+ maxChunkGensPerTick = Integer.MAX_VALUE;
|
||||
+ log("Max Chunk Gens Per Tick: Unlimited (NOT RECOMMENDED)");
|
||||
+ } else {
|
||||
+ log("Max Chunk Gens Per Tick: " + maxChunkGensPerTick);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
||||
index 395386f29..baf72404e 100644
|
||||
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
||||
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
||||
@@ -27,6 +27,7 @@ public class PlayerChunk {
|
||||
private boolean done;
|
||||
|
||||
// CraftBukkit start - add fields
|
||||
+ boolean chunkExists; // Paper
|
||||
private boolean loadInProgress = false;
|
||||
private Runnable loadedRunnable = new Runnable() {
|
||||
public void run() {
|
||||
@@ -50,6 +51,7 @@ public class PlayerChunk {
|
||||
// CraftBukkit start
|
||||
loadInProgress = true;
|
||||
this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false);
|
||||
+ this.chunkExists = this.chunk != null || ChunkIOExecutor.hasQueuedChunkLoad(playerChunkMap.getWorld(), i, j); // Paper
|
||||
markChunkUsed(); // Paper - delay chunk unloads
|
||||
// CraftBukkit end
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
index 6ee9f6cfb..99652ae3e 100644
|
||||
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||
@@ -166,6 +166,7 @@ public class PlayerChunkMap {
|
||||
// Spigot start
|
||||
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||
activityAccountant.startActivity(0.5);
|
||||
+ int chunkGensAllowed = world.paperConfig.maxChunkGensPerTick; // Paper
|
||||
// Spigot end
|
||||
|
||||
Iterator iterator1 = this.h.iterator();
|
||||
@@ -175,6 +176,11 @@ public class PlayerChunkMap {
|
||||
|
||||
if (playerchunk1.f() == null) {
|
||||
boolean flag = playerchunk1.a(PlayerChunkMap.b);
|
||||
+ // Paper start
|
||||
+ if (flag && !playerchunk1.chunkExists && chunkGensAllowed-- <= 0) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
if (playerchunk1.a(flag)) {
|
||||
iterator1.remove();
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java
|
||||
index 9aaca21a7..f50d55c8e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java
|
||||
@@ -35,4 +35,10 @@ public class ChunkIOExecutor {
|
||||
public static void tick() {
|
||||
instance.finishActive();
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public static boolean hasQueuedChunkLoad(World world, int x, int z) {
|
||||
+ return instance.hasTask(new QueuedChunk(x, z, null, world, null));
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java b/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java
|
||||
index 193c3621c..cf1258c55 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java
|
||||
@@ -351,4 +351,10 @@ public final class AsynchronousExecutor<P, T, C, E extends Throwable> {
|
||||
public void setActiveThreads(final int coreSize) {
|
||||
pool.setCorePoolSize(coreSize);
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ public boolean hasTask(P parameter) throws IllegalStateException {
|
||||
+ return tasks.get(parameter) != null;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
--
|
||||
2.18.0
|
||||
|
Loading…
Reference in New Issue
Block a user