diff options
author | Bjarne Koll <[email protected]> | 2024-09-19 16:36:07 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-09-19 16:36:07 +0200 |
commit | c5a10665b8b80af650500b9263036f778f06d500 (patch) | |
tree | fedc133f0dbc101067951e1fccd9d577c312fdb8 /patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch | |
parent | 5c829557332f21b34bc81e6ad1a73e511faef8f6 (diff) | |
download | Paper-c5a10665b8b80af650500b9263036f778f06d500.tar.gz Paper-c5a10665b8b80af650500b9263036f778f06d500.zip |
Remove wall-time / unused skip tick protection (#11412)
Spigot still maintains some partial implementation of "tick skipping", a
practice in which the MinecraftServer.currentTick field is updated not
by an increment of one per actual tick, but instead set to
System.currentTimeMillis() / 50. This behaviour means that the tracked
tick may "skip" a tick value in case a previous tick took more than the
expected 50ms.
To compensate for this in important paths, spigot/craftbukkit
implements "wall-time". Instead of incrementing/decrementing ticks on
block entities/entities by one for each call to their tick() method,
they instead increment/decrement important values, like
an ItemEntity's age or pickupDelay, by the difference of
`currentTick - lastTick`, where `lastTick` is the value of
`currentTick` during the last tick() call.
These "fixes" however do not play nicely with minecraft's simulation
distance as entities/block entities implementing the above behaviour
would "catch up" their values when moving from a non-ticking chunk to a
ticking one as their `lastTick` value remains stuck on the last tick in
a ticking chunk and hence lead to a large "catch up" once ticked again.
Paper completely removes the "tick skipping" behaviour (See patch
"Further-improve-server-tick-loop"), making the above precautions
completely unnecessary, which also rids paper of the previous described
incompatibility with non-ticking chunks.
Diffstat (limited to 'patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch')
-rw-r--r-- | patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch b/patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch new file mode 100644 index 0000000000..0e609e40fd --- /dev/null +++ b/patches/server/1018-Proxy-ItemStack-to-CraftItemStack.patch @@ -0,0 +1,300 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 14 May 2024 11:57:43 -0700 +Subject: [PATCH] Proxy ItemStack to CraftItemStack + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +index aef5c0d1be9c4aa62d9b7c5cacbb42066a802729..db0fe1b755f59eafca6e57917429fb7889889c3a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +@@ -26,15 +26,57 @@ import org.jetbrains.annotations.ApiStatus; + @DelegateDeserialization(ItemStack.class) + public final class CraftItemStack extends ItemStack { + +- // Paper start - MC Utils +- public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) { +- if (bukkit instanceof CraftItemStack craftItemStack) { +- return craftItemStack.handle != null ? craftItemStack.handle : net.minecraft.world.item.ItemStack.EMPTY; ++ // Paper start - delegate api-ItemStack to CraftItemStack ++ private static final java.lang.invoke.VarHandle API_ITEM_STACK_CRAFT_DELEGATE_FIELD; ++ static { ++ try { ++ API_ITEM_STACK_CRAFT_DELEGATE_FIELD = java.lang.invoke.MethodHandles.privateLookupIn( ++ ItemStack.class, ++ java.lang.invoke.MethodHandles.lookup() ++ ).findVarHandle(ItemStack.class, "craftDelegate", ItemStack.class); ++ } catch (final IllegalAccessException | NoSuchFieldException exception) { ++ throw new RuntimeException(exception); ++ } ++ } ++ ++ private static CraftItemStack getCraftStack(final ItemStack bukkit) { ++ if (bukkit instanceof final CraftItemStack craftItemStack) { ++ return craftItemStack; + } else { +- return asNMSCopy(bukkit); ++ return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit); + } + } + ++ @Override ++ public int hashCode() { ++ if (this.handle == null || this.handle.isEmpty()) { ++ return net.minecraft.world.item.ItemStack.EMPTY.hashCode(); ++ } else { ++ int hash = net.minecraft.world.item.ItemStack.hashItemAndComponents(this.handle); ++ hash = hash * 31 + this.handle.getCount(); ++ return hash; ++ } ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false; ++ final CraftItemStack craftStack = getCraftStack(bukkit); ++ if (this.handle == craftStack.handle) return true; ++ else if (this.handle == null || craftStack.handle == null) return false; ++ else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true; ++ else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle); ++ } ++ // Paper end ++ ++ // Paper start - MC Utils ++ public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) { ++ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack ++ final CraftItemStack craftItemStack = getCraftStack(bukkit); ++ return craftItemStack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : craftItemStack.handle; ++ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack ++ } ++ + public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack old, ItemStack newInstance) { + return old == newInstance ? unwrap(old) : asNMSCopy(newInstance); + } +@@ -48,25 +90,13 @@ public final class CraftItemStack extends ItemStack { + // Paper end - override isEmpty to use vanilla's impl + + public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) { +- if (original instanceof CraftItemStack) { +- CraftItemStack stack = (CraftItemStack) original; +- return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy(); +- } +- if (original == null || original.isEmpty()) { // Paper - override isEmpty to use vanilla's impl; use isEmpty ++ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack ++ if (original == null || original.isEmpty()) { + return net.minecraft.world.item.ItemStack.EMPTY; + } +- +- Item item = CraftItemType.bukkitToMinecraft(original.getType()); +- +- if (item == null) { +- return net.minecraft.world.item.ItemStack.EMPTY; +- } +- +- net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(item, original.getAmount()); +- if (original.hasItemMeta()) { +- CraftItemStack.setItemMeta(stack, original.getItemMeta()); +- } +- return stack; ++ final CraftItemStack stack = getCraftStack(original); ++ return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy(); ++ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack + } + + // Paper start +@@ -89,14 +119,10 @@ public final class CraftItemStack extends ItemStack { + * Copies the NMS stack to return as a strictly-Bukkit stack + */ + public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStack original) { +- if (original.isEmpty()) { +- return new ItemStack(Material.AIR); +- } +- ItemStack stack = new ItemStack(CraftItemType.minecraftToBukkit(original.getItem()), original.getCount()); +- if (CraftItemStack.hasItemMeta(original)) { +- stack.setItemMeta(CraftItemStack.getItemMeta(original)); +- } +- return stack; ++ // Paper start - no such thing as a "strictly-Bukkit stack" anymore ++ // we copy the stack since it should be a complete copy not a mirror ++ return asCraftMirror(original.copy()); ++ // Paper end + } + + public static CraftItemStack asCraftMirror(net.minecraft.world.item.ItemStack original) { +@@ -317,11 +343,7 @@ public final class CraftItemStack extends ItemStack { + + @Override + public CraftItemStack clone() { +- CraftItemStack itemStack = (CraftItemStack) super.clone(); +- if (this.handle != null) { +- itemStack.handle = this.handle.copy(); +- } +- return itemStack; ++ return new org.bukkit.craftbukkit.inventory.CraftItemStack(this.handle != null ? this.handle.copy() : null); // Paper + } + + @Override +@@ -424,22 +446,14 @@ public final class CraftItemStack extends ItemStack { + if (stack == this) { + return true; + } +- if (!(stack instanceof CraftItemStack)) { +- return stack.getClass() == ItemStack.class && stack.isSimilar(this); +- } +- +- CraftItemStack that = (CraftItemStack) stack; ++ final CraftItemStack that = getCraftStack(stack); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack + if (this.handle == that.handle) { + return true; + } + if (this.handle == null || that.handle == null) { + return false; + } +- Material comparisonType = CraftLegacy.fromLegacy(that.getType()); // This may be called from legacy item stacks, try to get the right material +- if (!(comparisonType == this.getType() && this.getDurability() == that.getDurability())) { +- return false; +- } +- return this.hasItemMeta() ? that.hasItemMeta() && this.handle.getComponents().equals(that.handle.getComponents()) : !that.hasItemMeta(); ++ return net.minecraft.world.item.ItemStack.isSameItemSameComponents(this.handle, that.handle); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +index d03f4a767f6c7fe7d6bcef20e6676c39d9657584..bae3dd5fc67e6b3d98a5e63ffbf639c5042f8843 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +@@ -100,13 +100,14 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han + @NotNull + @Override + public ItemStack createItemStack(final int amount, @Nullable final Consumer<? super M> metaConfigurator) { +- final ItemStack itemStack = new ItemStack(this.asMaterial(), amount); ++ // Paper start - re-implement to return CraftItemStack ++ final net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(this.item, amount); ++ final CraftItemStack mirror = CraftItemStack.asCraftMirror(stack); + if (metaConfigurator != null) { +- final ItemMeta itemMeta = itemStack.getItemMeta(); +- metaConfigurator.accept((M) itemMeta); +- itemStack.setItemMeta(itemMeta); ++ mirror.editMeta(this.getItemMetaClass(), metaConfigurator); + } +- return itemStack; ++ return mirror; ++ // Paper start - reimplement to return CraftItemStack + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +index 6cc9d7a9e6d4bfdc27e52fc581b2bb832616f121..6930d0afb230a88aa813b02e4d55c95d3a049688 100644 +--- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java ++++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java +@@ -678,4 +678,16 @@ public class MaterialRerouting { + return itemStack.withType(material); + } + // Paper end - register paper API specific material consumers in rerouting ++ ++ // Paper start - methods added post 1.13, no-op ++ @RerouteStatic("org/bukkit/inventory/ItemStack") ++ public static ItemStack of(final Material material) { ++ return ItemStack.of(material); ++ } ++ ++ @RerouteStatic("org/bukkit/inventory/ItemStack") ++ public static ItemStack of(final Material material, final int amount) { ++ return ItemStack.of(material, amount); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 82ebfd09e9baca0a31ee41c0e5228bce3c54e74f..28dbe30a98a6730839949bc9a6a90b78619ff84d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -683,6 +683,13 @@ public final class CraftMagicNumbers implements UnsafeValues { + } + // Paper end - hack to get tags for non server-backed registries + ++ // Paper start - proxy ItemStack ++ @Override ++ public org.bukkit.inventory.ItemStack createEmptyStack() { ++ return CraftItemStack.asCraftMirror(null); ++ } ++ // Paper end - proxy ItemStack ++ + /** + * This helper class represents the different NBT Tags. + * <p> +diff --git a/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e5c2fb160e9d390cdfa0259a3feb9f488b2dc14d +--- /dev/null ++++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java +@@ -0,0 +1,53 @@ ++package io.papermc.paper.configuration; ++ ++import org.bukkit.Material; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.support.AbstractTestingBase; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertEquals; ++import static org.junit.jupiter.api.Assertions.assertFalse; ++import static org.junit.jupiter.api.Assertions.assertNull; ++import static org.junit.jupiter.api.Assertions.assertTrue; ++ ++public abstract class ConfigurationSectionTest extends AbstractTestingBase { ++ public abstract ConfigurationSection getConfigurationSection(); ++ ++ @Test ++ public void testGetItemStack_String() { ++ ConfigurationSection section = getConfigurationSection(); ++ String key = "exists"; ++ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50); ++ ++ section.set(key, value); ++ ++ assertEquals(value, section.getItemStack(key)); ++ assertNull(section.getString("doesntExist")); ++ } ++ ++ @Test ++ public void testGetItemStack_String_ItemStack() { ++ ConfigurationSection section = getConfigurationSection(); ++ String key = "exists"; ++ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50); ++ ItemStack def = new ItemStack(Material.STONE, 1); ++ ++ section.set(key, value); ++ ++ assertEquals(value, section.getItemStack(key, def)); ++ assertEquals(def, section.getItemStack("doesntExist", def)); ++ } ++ ++ @Test ++ public void testIsItemStack() { ++ ConfigurationSection section = getConfigurationSection(); ++ String key = "exists"; ++ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50); ++ ++ section.set(key, value); ++ ++ assertTrue(section.isItemStack(key)); ++ assertFalse(section.isItemStack("doesntExist")); ++ } ++} +diff --git a/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..def33c36f207a4c5306b5a895336aa70335c1678 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java +@@ -0,0 +1,11 @@ ++package io.papermc.paper.configuration; ++ ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemoryConfiguration; ++ ++public class MemorySectionTest extends ConfigurationSectionTest { ++ @Override ++ public ConfigurationSection getConfigurationSection() { ++ return new MemoryConfiguration().createSection("section"); ++ } ++} |