diff options
Diffstat (limited to 'patches/unapplied/server')
5 files changed, 1211 insertions, 0 deletions
diff --git a/patches/unapplied/server/0277-Improve-exact-choice-recipe-ingredients.patch b/patches/unapplied/server/0277-Improve-exact-choice-recipe-ingredients.patch new file mode 100644 index 0000000000..9c0b7b8275 --- /dev/null +++ b/patches/unapplied/server/0277-Improve-exact-choice-recipe-ingredients.patch @@ -0,0 +1,436 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 25 Jun 2023 23:10:14 -0700 +Subject: [PATCH] Improve exact choice recipe ingredients + +Fixes exact choices not working with recipe book clicks +and shapeless recipes. + +== AT == +public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG +public net.minecraft.world.entity.player.StackedContents put(II)V +public net.minecraft.world.entity.player.StackedContents take(II)I + +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ef68600f6b59674ddea6c77f7e412902888e39b7 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.inventory.recipe; ++ ++import net.minecraft.world.Container; ++import net.minecraft.world.item.crafting.Ingredient; ++import net.minecraft.world.item.crafting.Recipe; ++ ++public abstract class RecipeBookExactChoiceRecipe<C extends net.minecraft.world.item.crafting.RecipeInput> implements Recipe<C> { ++ ++ private boolean hasExactIngredients; ++ ++ protected final void checkExactIngredients() { ++ // skip any special recipes ++ if (this.isSpecial()) { ++ this.hasExactIngredients = false; ++ return; ++ } ++ for (final Ingredient ingredient : this.getIngredients()) { ++ if (!ingredient.isEmpty() && ingredient.exact) { ++ this.hasExactIngredients = true; ++ return; ++ } ++ } ++ this.hasExactIngredients = false; ++ } ++ ++ @Override ++ public final boolean hasExactIngredients() { ++ return this.hasExactIngredients; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..568ba6aed2e74b8d84f4e82c1e785ef1587e2617 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +@@ -0,0 +1,109 @@ ++package io.papermc.paper.inventory.recipe; ++ ++import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; ++import it.unimi.dsi.fastutil.ints.Int2IntMap; ++import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ++import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntComparators; ++import it.unimi.dsi.fastutil.ints.IntList; ++import it.unimi.dsi.fastutil.objects.Object2IntMap; ++import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; ++import java.util.IdentityHashMap; ++import java.util.Map; ++import java.util.concurrent.atomic.AtomicInteger; ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.world.entity.player.StackedContents; ++import net.minecraft.world.item.ItemStack; ++import net.minecraft.world.item.ItemStackLinkedSet; ++import net.minecraft.world.item.crafting.CraftingInput; ++import net.minecraft.world.item.crafting.Ingredient; ++import net.minecraft.world.item.crafting.Recipe; ++ ++public final class StackedContentsExtraMap { ++ ++ private final AtomicInteger idCounter = new AtomicInteger(BuiltInRegistries.ITEM.size()); // start at max vanilla stacked contents idx ++ public final Object2IntMap<ItemStack> exactChoiceIds = new Object2IntOpenCustomHashMap<>(ItemStackLinkedSet.TYPE_AND_TAG); ++ private final Int2ObjectMap<ItemStack> idToExactChoice = new Int2ObjectOpenHashMap<>(); ++ private final StackedContents contents; ++ public final Map<Ingredient, IntList> extraStackingIds = new IdentityHashMap<>(); ++ ++ public StackedContentsExtraMap(final StackedContents contents, final Recipe<?> recipe) { ++ this.exactChoiceIds.defaultReturnValue(-1); ++ this.contents = contents; ++ this.initialize(recipe); ++ } ++ ++ private void initialize(final Recipe<?> recipe) { ++ if (recipe.hasExactIngredients()) { ++ for (final Ingredient ingredient : recipe.getIngredients()) { ++ if (!ingredient.isEmpty() && ingredient.exact) { ++ final net.minecraft.world.item.ItemStack[] items = ingredient.getItems(); ++ final IntList idList = new IntArrayList(items.length); ++ for (final ItemStack item : items) { ++ idList.add(this.registerExact(item)); // I think not copying the stack here is safe because cb copies the stack when creating the ingredient ++ if (item.getComponentsPatch().isEmpty()) { ++ // add regular index if it's a plain itemstack but still registered as exact ++ idList.add(StackedContents.getStackingIndex(item)); ++ } ++ } ++ idList.sort(IntComparators.NATURAL_COMPARATOR); ++ this.extraStackingIds.put(ingredient, idList); ++ } ++ } ++ } ++ } ++ ++ private int registerExact(final ItemStack exactChoice) { ++ final int existing = this.exactChoiceIds.getInt(exactChoice); ++ if (existing > -1) { ++ return existing; ++ } ++ final int id = this.idCounter.getAndIncrement(); ++ this.exactChoiceIds.put(exactChoice, id); ++ this.idToExactChoice.put(id, exactChoice); ++ return id; ++ } ++ ++ public ItemStack getById(int id) { ++ return this.idToExactChoice.get(id); ++ } ++ ++ public Int2IntMap regularRemoved = new Int2IntArrayMap(); ++ public void accountInput(final CraftingInput input) { ++ // similar logic to the CraftingInput constructor ++ for (final ItemStack item : input.items()) { ++ if (!item.isEmpty()) { ++ if (this.accountStack(item, 1)) { ++ // remove one of the items if it was added to the contents as a non-extra item ++ final int plainStackIdx = StackedContents.getStackingIndex(item); ++ if (this.contents.take(plainStackIdx, 1) == plainStackIdx) { ++ this.regularRemoved.put(plainStackIdx, 1); ++ } ++ } ++ } ++ } ++ } ++ ++ public void resetExtras() { ++ // clear previous extra ids ++ for (final int extraId : this.exactChoiceIds.values()) { ++ this.contents.contents.remove(extraId); ++ } ++ for (final Int2IntMap.Entry entry : this.regularRemoved.int2IntEntrySet()) { ++ this.contents.put(entry.getIntKey(), entry.getIntValue()); ++ } ++ } ++ ++ public boolean accountStack(final ItemStack stack, final int count) { ++ if (!this.exactChoiceIds.isEmpty()) { ++ final int id = this.exactChoiceIds.getInt(stack); ++ if (id >= 0) { ++ this.contents.put(id, count); ++ return true; ++ } ++ } ++ return false; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/package-info.java b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java +new file mode 100644 +index 0000000000000000000000000000000000000000..413dfa52760db393ad6a8b5341200ee704a864fc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java +@@ -0,0 +1,5 @@ ++@DefaultQualifier(NonNull.class) ++package io.papermc.paper.inventory.recipe; ++ ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; +diff --git a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java +index 0bd749af8014dd437229594ef6981a2ead803990..6d1f9c15dc99917a2ac966ea38ef1970f4f0289c 100644 +--- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java ++++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java +@@ -31,6 +31,7 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple + this.inventory = entity.getInventory(); + if (this.testClearGrid() || entity.isCreative()) { + this.stackedContents.clear(); ++ this.stackedContents.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients + entity.getInventory().fillStackedContents(this.stackedContents); + this.menu.fillCraftSlotsStackedContents(this.stackedContents); + if (this.stackedContents.canCraft(recipe.value(), null)) { +@@ -77,7 +78,7 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple + int l = k; + + for (int m : intList) { +- ItemStack itemStack2 = StackedContents.fromStackingIndex(m); ++ ItemStack itemStack2 = StackedContents.fromStackingIndexWithExtras(m, this.stackedContents); // Paper - Improve exact choice recipe ingredients + if (!itemStack2.isEmpty()) { + int n = itemStack2.getMaxStackSize(); + if (n < l) { +@@ -96,12 +97,22 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple + @Override + public void addItemToSlot(Integer input, int slot, int amount, int gridX, int gridY) { + Slot slot2 = this.menu.getSlot(slot); +- ItemStack itemStack = StackedContents.fromStackingIndex(input); ++ // Paper start - Improve exact choice recipe ingredients ++ ItemStack itemStack = null; ++ boolean isExact = false; ++ if (this.stackedContents.extrasMap != null && input >= net.minecraft.core.registries.BuiltInRegistries.ITEM.size()) { ++ itemStack = StackedContents.fromStackingIndexExtras(input, this.stackedContents.extrasMap).copy(); ++ isExact = true; ++ } ++ if (itemStack == null) { ++ itemStack = StackedContents.fromStackingIndex(input); ++ } ++ // Paper end - Improve exact choice recipe ingredients + if (!itemStack.isEmpty()) { + int i = amount; + + while (i > 0) { +- i = this.moveItemToGrid(slot2, itemStack, i); ++ i = this.moveItemToGrid(slot2, itemStack, i, isExact); // Paper - Improve exact choice recipe ingredients + if (i == -1) { + return; + } +@@ -133,8 +144,15 @@ public class ServerPlaceRecipe<I extends RecipeInput, R extends Recipe<I>> imple + return i; + } + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Improve exact choice recipe ingredients ++ + protected int moveItemToGrid(Slot slot, ItemStack stack, int i) { +- int j = this.inventory.findSlotMatchingUnusedItem(stack); ++ // Paper start - Improve exact choice recipe ingredients ++ return this.moveItemToGrid(slot, stack, i, false); ++ } ++ protected int moveItemToGrid(Slot slot, ItemStack stack, int i, final boolean isExact) { ++ int j = isExact ? this.inventory.findSlotMatchingItem(stack) : this.inventory.findSlotMatchingUnusedItem(stack); ++ // Paper end - Improve exact choice recipe ingredients + if (j == -1) { + return -1; + } else { +diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java +index fa5576e41baec4b52c7ebb877924eb91d3775a2d..fcabf630ce1e4949d00f485a5bff66dd1e54a277 100644 +--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java ++++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java +@@ -22,8 +22,10 @@ import net.minecraft.world.item.crafting.RecipeHolder; + public class StackedContents { + private static final int EMPTY = 0; + public final Int2IntMap contents = new Int2IntOpenHashMap(); ++ @Nullable public io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap = null; // Paper - Improve exact choice recipe ingredients + + public void accountSimpleStack(ItemStack stack) { ++ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; max of 64 due to accountStack method below + if (!stack.isDamaged() && !stack.isEnchanted() && !stack.has(DataComponents.CUSTOM_NAME)) { + this.accountStack(stack); + } +@@ -37,6 +39,7 @@ public class StackedContents { + if (!stack.isEmpty()) { + int i = getStackingIndex(stack); + int j = Math.min(maxCount, stack.getCount()); ++ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, j)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it + this.put(i, j); + } + } +@@ -83,6 +86,31 @@ public class StackedContents { + return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId)); + } + ++ // Paper start - Improve exact choice recipe ingredients ++ public void initializeExtras(final Recipe<?> recipe, @Nullable final net.minecraft.world.item.crafting.CraftingInput input) { ++ this.extrasMap = new io.papermc.paper.inventory.recipe.StackedContentsExtraMap(this, recipe); ++ if (input != null) this.extrasMap.accountInput(input); ++ } ++ ++ public void resetExtras() { ++ if (this.extrasMap != null && !this.contents.isEmpty()) { ++ this.extrasMap.resetExtras(); ++ } ++ this.extrasMap = null; ++ } ++ ++ public static ItemStack fromStackingIndexWithExtras(final int itemId, @Nullable final StackedContents contents) { ++ if (contents != null && contents.extrasMap != null && itemId >= BuiltInRegistries.ITEM.size()) { ++ return fromStackingIndexExtras(itemId, contents.extrasMap); ++ } ++ return fromStackingIndex(itemId); ++ } ++ ++ public static ItemStack fromStackingIndexExtras(final int itemId, final io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap) { ++ return extrasMap.getById(itemId).copy(); ++ } ++ // Paper end - Improve exact choice recipe ingredients ++ + public void clear() { + this.contents.clear(); + } +@@ -106,7 +134,7 @@ public class StackedContents { + this.data = new BitSet(this.ingredientCount + this.itemCount + this.ingredientCount + this.ingredientCount * this.itemCount); + + for (int i = 0; i < this.ingredients.size(); i++) { +- IntList intList = this.ingredients.get(i).getStackingIds(); ++ IntList intList = this.getStackingIds(this.ingredients.get(i)); // Paper - Improve exact choice recipe ingredients + + for (int j = 0; j < this.itemCount; j++) { + if (intList.contains(this.items[j])) { +@@ -169,7 +197,7 @@ public class StackedContents { + IntCollection intCollection = new IntAVLTreeSet(); + + for (Ingredient ingredient : this.ingredients) { +- intCollection.addAll(ingredient.getStackingIds()); ++ intCollection.addAll(this.getStackingIds(ingredient)); // Paper - Improve exact choice recipe ingredients + } + + IntIterator intIterator = intCollection.iterator(); +@@ -298,7 +326,7 @@ public class StackedContents { + for (Ingredient ingredient : this.ingredients) { + int j = 0; + +- for (int k : ingredient.getStackingIds()) { ++ for (int k : this.getStackingIds(ingredient)) { // Paper - Improve exact choice recipe ingredients + j = Math.max(j, StackedContents.this.contents.get(k)); + } + +@@ -309,5 +337,17 @@ public class StackedContents { + + return i; + } ++ ++ // Paper start - Improve exact choice recipe ingredients ++ private IntList getStackingIds(final Ingredient ingredient) { ++ if (StackedContents.this.extrasMap != null) { ++ final IntList ids = StackedContents.this.extrasMap.extraStackingIds.get(ingredient); ++ if (ids != null) { ++ return ids; ++ } ++ } ++ return ingredient.getStackingIds(); ++ } ++ // Paper end - Improve exact choice recipe ingredients + } + } +diff --git a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java +index f3b6466089ee8be59747a16aac2cac84be30617d..45c80500201aabc1e8643427ebfb8818ab966750 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java +@@ -5,7 +5,7 @@ import net.minecraft.core.NonNullList; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.Level; + +-public abstract class AbstractCookingRecipe implements Recipe<SingleRecipeInput> { ++public abstract class AbstractCookingRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<SingleRecipeInput> implements Recipe<SingleRecipeInput> { // Paper - improve exact recipe choices + protected final RecipeType<?> type; + protected final CookingBookCategory category; + protected final String group; +@@ -24,6 +24,7 @@ public abstract class AbstractCookingRecipe implements Recipe<SingleRecipeInput> + this.result = result; + this.experience = experience; + this.cookingTime = cookingTime; ++ this.checkExactIngredients(); // Paper - improve exact recipe choices + } + + @Override +diff --git a/src/main/java/net/minecraft/world/item/crafting/Recipe.java b/src/main/java/net/minecraft/world/item/crafting/Recipe.java +index 3cab383e01c124349f3f96bcbcfe91356d51aa30..b57568d5e9c4c148a4b3c303c925a813fdd5dc67 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java +@@ -73,4 +73,10 @@ public interface Recipe<T extends RecipeInput> { + } + + org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit ++ ++ // Paper start - improved exact choice recipes ++ default boolean hasExactIngredients() { ++ return false; ++ } ++ // Paper end + } +diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java +index 59372daacd6fef45373c0557ccebb6ff5f16f174..63cf2b66f51df68aa3f6d98c69368ce454869d64 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java +@@ -17,7 +17,7 @@ import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; + import org.bukkit.inventory.RecipeChoice; + // CraftBukkit end + +-public class ShapedRecipe implements CraftingRecipe { ++public class ShapedRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingInput> implements CraftingRecipe { // Paper - improve exact recipe choices + + final ShapedRecipePattern pattern; + final ItemStack result; +@@ -31,6 +31,7 @@ public class ShapedRecipe implements CraftingRecipe { + this.pattern = raw; + this.result = result; + this.showNotification = showNotification; ++ this.checkExactIngredients(); // Paper - improve exact recipe choices + } + + public ShapedRecipe(String group, CraftingBookCategory category, ShapedRecipePattern raw, ItemStack result) { +diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..213ee4aa988dd4c2a5a7be99b1d13f67338e5209 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +@@ -19,7 +19,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe; + import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; + // CraftBukkit end + +-public class ShapelessRecipe implements CraftingRecipe { ++public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingInput> implements CraftingRecipe { // Paper - improve exact recipe choices + + final String group; + final CraftingBookCategory category; +@@ -31,6 +31,7 @@ public class ShapelessRecipe implements CraftingRecipe { + this.category = category; + this.result = result; + this.ingredients = ingredients; ++ this.checkExactIngredients(); // Paper - improve exact recipe choices + } + + // CraftBukkit start +@@ -75,7 +76,18 @@ public class ShapelessRecipe implements CraftingRecipe { + } + + public boolean matches(CraftingInput input, Level world) { +- return input.ingredientCount() != this.ingredients.size() ? false : (input.size() == 1 && this.ingredients.size() == 1 ? ((Ingredient) this.ingredients.getFirst()).test(input.getItem(0)) : input.stackedContents().canCraft(this, (IntList) null)); ++ // Paper start - unwrap ternary & better exact choice recipes ++ if (input.ingredientCount() != this.ingredients.size()) { ++ return false; ++ } ++ if (input.size() == 1 && this.ingredients.size() == 1) { ++ return this.ingredients.getFirst().test(input.getItem(0)); ++ } ++ input.stackedContents().initializeExtras(this, input); // setup stacked contents for this recipe ++ final boolean canCraft = input.stackedContents().canCraft(this, null); ++ input.stackedContents().resetExtras(); ++ return canCraft; ++ // Paper end - unwrap ternary & better exact choice recipes + } + + public ItemStack assemble(CraftingInput input, HolderLookup.Provider lookup) { diff --git a/patches/unapplied/server/0451-Fix-harming-potion-dupe.patch b/patches/unapplied/server/0451-Fix-harming-potion-dupe.patch new file mode 100644 index 0000000000..b184bf641c --- /dev/null +++ b/patches/unapplied/server/0451-Fix-harming-potion-dupe.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: PepperCode1 <[email protected]> +Date: Thu, 23 Jul 2020 14:25:07 -0700 +Subject: [PATCH] Fix harming potion dupe + +EntityLiving#applyInstantEffect() immediately kills the player and drops their inventory. +Before this patch, instant effects would be applied before the potion ItemStack is removed and replaced with a glass bottle. This caused the potion ItemStack to be dropped before it was supposed to be removed from the inventory. It also caused the glass bottle to be put into a dead player's inventory. +This patch makes it so that instant effects are applied after the potion ItemStack is removed, and the glass bottle is only put into the player's inventory if the player is not dead. Otherwise, the glass bottle is dropped on the ground. + +diff --git a/src/main/java/net/minecraft/world/item/PotionItem.java b/src/main/java/net/minecraft/world/item/PotionItem.java +index 017b0745753ea1907bad7022405db9a5a487d069..92fa6523f2bba105a74fff228e36e58666ed56ae 100644 +--- a/src/main/java/net/minecraft/world/item/PotionItem.java ++++ b/src/main/java/net/minecraft/world/item/PotionItem.java +@@ -55,12 +55,13 @@ public class PotionItem extends Item { + CriteriaTriggers.CONSUME_ITEM.trigger((ServerPlayer) entityhuman, stack); + } + ++ List<net.minecraft.world.effect.MobEffectInstance> instantLater = new java.util.ArrayList<>(); // Paper - Fix harming potion dupe + if (!world.isClientSide) { + PotionContents potioncontents = (PotionContents) stack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + + potioncontents.forEachEffect((mobeffect) -> { + if (((MobEffect) mobeffect.getEffect().value()).isInstantenous()) { +- ((MobEffect) mobeffect.getEffect().value()).applyInstantenousEffect(entityhuman, entityhuman, user, mobeffect.getAmplifier(), 1.0D); ++ instantLater.add(mobeffect); // Paper + } else { + user.addEffect(mobeffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_DRINK); // CraftBukkit + } +@@ -73,7 +74,18 @@ public class PotionItem extends Item { + stack.consume(1, entityhuman); + } + ++ // Paper start - Fix harming potion dupe ++ for (net.minecraft.world.effect.MobEffectInstance mobeffect : instantLater) { ++ mobeffect.getEffect().value().applyInstantenousEffect(entityhuman, entityhuman, user, mobeffect.getAmplifier(), 1.0D); ++ } ++ // Paper end - Fix harming potion dupe + if (entityhuman == null || !entityhuman.hasInfiniteMaterials()) { ++ // Paper start - Fix harming potion dupe ++ if (user.getHealth() <= 0 && !user.level().getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_KEEPINVENTORY)) { ++ user.spawnAtLocation(new ItemStack(Items.GLASS_BOTTLE), 0); ++ return ItemStack.EMPTY; ++ } ++ // Paper end - Fix harming potion dupe + if (stack.isEmpty()) { + return new ItemStack(Items.GLASS_BOTTLE); + } diff --git a/patches/unapplied/server/0777-Fix-inconsistencies-in-dispense-events-regarding-sta.patch b/patches/unapplied/server/0777-Fix-inconsistencies-in-dispense-events-regarding-sta.patch new file mode 100644 index 0000000000..8b797606ed --- /dev/null +++ b/patches/unapplied/server/0777-Fix-inconsistencies-in-dispense-events-regarding-sta.patch @@ -0,0 +1,456 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 11 Dec 2022 23:47:22 -0800 +Subject: [PATCH] Fix inconsistencies in dispense events regarding stack size + +The javadocs for BlockDispenseEvent suggest the ItemStack is a single +item which is being dispensed. Before this fix, sometimes it was the whole +stack before a single item had been taken. This fixes that so the stack size +is always 1. + +diff --git a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java +index 90e1914599b43c8bf813596b3b428d8be3bac1b5..6df0db8b4cdab23494ea34236949ece4989110a3 100644 +--- a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java +@@ -58,7 +58,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior { + + // Object object = this.isChestBoat ? new ChestBoat(worldserver, d1, d2 + d4, d3) : new EntityBoat(worldserver, d1, d2 + d4, d3); + // CraftBukkit start +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -68,12 +68,13 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -89,8 +90,7 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior { + EntityType.createDefaultStackConfig(worldserver, stack, (Player) null).accept(object); + ((Boat) object).setVariant(this.type); + ((Boat) object).setYRot(enumdirection.toYRot()); +- if (!worldserver.addFreshEntity((Entity) object)) stack.grow(1); // CraftBukkit +- // itemstack.shrink(1); // CraftBukkit - handled during event processing ++ if (worldserver.addFreshEntity((Entity) object) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink + return stack; + } + +diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java +index fb80b00b34ae5a4b1491c618a7fe1bdbbde0de9b..96db0b1041a4c0f054d4f3f2bdced960b119664e 100644 +--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java +@@ -110,7 +110,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + ServerLevel worldserver = pointer.level(); +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -120,12 +120,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -142,7 +143,7 @@ public interface DispenseItemBehavior { + return ItemStack.EMPTY; + } + +- // itemstack.shrink(1); // Handled during event processing ++ if (shrink) stack.shrink(1); // Paper - actually handle here + // CraftBukkit end + pointer.level().gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, pointer.pos()); + return stack; +@@ -164,7 +165,7 @@ public interface DispenseItemBehavior { + ServerLevel worldserver = pointer.level(); + + // CraftBukkit start +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -174,12 +175,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -196,7 +198,7 @@ public interface DispenseItemBehavior { + ArmorStand entityarmorstand = (ArmorStand) EntityType.ARMOR_STAND.spawn(worldserver, consumer, blockposition, MobSpawnType.DISPENSER, false, false); + + if (entityarmorstand != null) { +- // itemstack.shrink(1); // CraftBukkit - Handled during event processing ++ if (shrink) stack.shrink(1); // Paper - actually handle here + } + + return stack; +@@ -216,7 +218,7 @@ public interface DispenseItemBehavior { + + if (!list.isEmpty()) { + // CraftBukkit start +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event + ServerLevel world = pointer.level(); + org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); +@@ -227,12 +229,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -243,6 +246,7 @@ public interface DispenseItemBehavior { + } + ((Saddleable) list.get(0)).equipSaddle(itemstack1, SoundSource.BLOCKS); + // CraftBukkit end ++ if (shrink) stack.shrink(1); // Paper - actually handle here + this.setSuccess(true); + return stack; + } else { +@@ -270,7 +274,7 @@ public interface DispenseItemBehavior { + } while (!entityhorseabstract.isBodyArmorItem(stack) || entityhorseabstract.isWearingBodyArmor() || !entityhorseabstract.isTamed()); + + // CraftBukkit start +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event + ServerLevel world = pointer.level(); + org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); +@@ -281,12 +285,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -296,6 +301,7 @@ public interface DispenseItemBehavior { + } + } + ++ if (shrink) stack.shrink(1); // Paper - shrink here + entityhorseabstract.setBodyArmorItem(CraftItemStack.asNMSCopy(event.getItem())); + // CraftBukkit end + this.setSuccess(true); +@@ -342,7 +348,7 @@ public interface DispenseItemBehavior { + entityhorsechestedabstract = (AbstractChestedHorse) iterator1.next(); + // CraftBukkit start + } while (!entityhorsechestedabstract.isTamed()); +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below + ServerLevel world = pointer.level(); + org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); +@@ -353,10 +359,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { ++ // stack.grow(1); // Paper - shrink below (this was actually missing and should be here, added it commented out to be consistent) + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -368,7 +377,7 @@ public interface DispenseItemBehavior { + entityhorsechestedabstract.getSlot(499).set(CraftItemStack.asNMSCopy(event.getItem())); + // CraftBukkit end + +- // itemstack.shrink(1); // CraftBukkit - handled above ++ if (shrink) stack.shrink(1); // Paper - actually handle here + this.setSuccess(true); + return stack; + } +@@ -413,7 +422,7 @@ public interface DispenseItemBehavior { + if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) { + // Paper end - correctly check if the bucket place will succeed + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z)); + if (!DispenserBlock.eventFired) { +@@ -475,7 +484,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!DispenserBlock.eventFired) { +@@ -513,7 +522,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!DispenserBlock.eventFired) { +@@ -575,7 +584,7 @@ public interface DispenseItemBehavior { + BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING)); + // CraftBukkit start + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!DispenserBlock.eventFired) { +@@ -641,7 +650,7 @@ public interface DispenseItemBehavior { + // CraftBukkit start + // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null); + +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -651,12 +660,13 @@ public interface DispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -672,7 +682,7 @@ public interface DispenseItemBehavior { + worldserver.addFreshEntity(entitytntprimed); + worldserver.playSound((Player) null, entitytntprimed.getX(), entitytntprimed.getY(), entitytntprimed.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); + worldserver.gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, blockposition); +- // itemstack.shrink(1); // CraftBukkit - handled above ++ if (shrink) stack.shrink(1); // Paper - actually handle here + return stack; + } + }); +@@ -699,7 +709,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!DispenserBlock.eventFired) { +@@ -748,7 +758,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!DispenserBlock.eventFired) { +@@ -810,7 +820,7 @@ public interface DispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - only single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!DispenserBlock.eventFired) { +diff --git a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java +index 1b1c54ce8f187b968352d4aad05821ece182e20b..985954030654d521291cccbfc3ca49b67ee4357d 100644 +--- a/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java +@@ -40,7 +40,7 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior { + + // CraftBukkit start + // this.projectileItem.shoot(iprojectile, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -50,12 +50,13 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -69,7 +70,7 @@ public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior { + ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity()); + // CraftBukkit end + worldserver.addFreshEntity(iprojectile); +- // itemstack.shrink(1); // CraftBukkit - Handled during event processing ++ if (shrink) stack.shrink(1); // Paper - actually handle here + return stack; + } + +diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +index 445560d94086452ca4fbaf7792ff2b04c3ed3b73..f32f8d5cb22feb885a53d3b56c04ad4219d2bafa 100644 +--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java +@@ -38,7 +38,7 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior { + ServerLevel worldserver = pointer.level(); + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!DispenserBlock.eventFired) { +diff --git a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java +index f84987c36a16df19286d6f1badfb1ffb9cc7e770..6f2adf2334e35e8a617a4ced0c1af2abf32bbd8d 100644 +--- a/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java +@@ -34,7 +34,7 @@ public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior { + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = CraftBlock.at(pointer.level(), pointer.pos()); +- CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!DispenserBlock.eventFired) { +diff --git a/src/main/java/net/minecraft/world/item/ArmorItem.java b/src/main/java/net/minecraft/world/item/ArmorItem.java +index d481ec2eace5fca7f80f6d9254121afd680e7309..fb518f87cc4ccd810fb32cade2fdd7e09ab0abfc 100644 +--- a/src/main/java/net/minecraft/world/item/ArmorItem.java ++++ b/src/main/java/net/minecraft/world/item/ArmorItem.java +@@ -55,7 +55,7 @@ public class ArmorItem extends Item implements Equipable { + } else { + LivingEntity entityliving = (LivingEntity) list.get(0); + EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(armor); +- ItemStack itemstack1 = armor.split(1); ++ ItemStack itemstack1 = armor.copyWithCount(1); // Paper - shrink below and single item in event + // CraftBukkit start + Level world = pointer.level(); + org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos()); +@@ -67,12 +67,13 @@ public class ArmorItem extends Item implements Equipable { + } + + if (event.isCancelled()) { +- armor.grow(1); ++ // armor.grow(1); // Paper - shrink below + return false; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- armor.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -89,6 +90,7 @@ public class ArmorItem extends Item implements Equipable { + ((Mob) entityliving).setPersistenceRequired(); + } + ++ if (shrink) armor.shrink(1); // Paper + return true; + } + } +diff --git a/src/main/java/net/minecraft/world/item/MinecartItem.java b/src/main/java/net/minecraft/world/item/MinecartItem.java +index 727319e86aa77b5a67b4c4f03b1e9aba9fe6bcde..66074445d3908b9bb1c8d70e1e27d057720ec8e5 100644 +--- a/src/main/java/net/minecraft/world/item/MinecartItem.java ++++ b/src/main/java/net/minecraft/world/item/MinecartItem.java +@@ -66,7 +66,7 @@ public class MinecartItem extends Item { + + // CraftBukkit start + // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.createMinecart(worldserver, d0, d1 + d3, d2, ((ItemMinecart) itemstack.getItem()).type); +- ItemStack itemstack1 = stack.split(1); ++ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event + org.bukkit.block.Block block2 = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + +@@ -76,12 +76,13 @@ public class MinecartItem extends Item { + } + + if (event.isCancelled()) { +- stack.grow(1); ++ // stack.grow(1); // Paper - shrink below + return stack; + } + ++ boolean shrink = true; // Paper + if (!event.getItem().equals(craftItem)) { +- stack.grow(1); ++ shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem()); +@@ -94,8 +95,7 @@ public class MinecartItem extends Item { + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), ((MinecartItem) itemstack1.getItem()).type, itemstack1, (Player) null); + +- if (!worldserver.addFreshEntity(entityminecartabstract)) stack.grow(1); +- // itemstack.shrink(1); // CraftBukkit - handled during event processing ++ if (worldserver.addFreshEntity(entityminecartabstract) && shrink) stack.shrink(1); // Paper - actually handle here + // CraftBukkit end + return stack; + } diff --git a/patches/unapplied/server/0845-API-for-updating-recipes-on-clients.patch b/patches/unapplied/server/0845-API-for-updating-recipes-on-clients.patch new file mode 100644 index 0000000000..7cae857359 --- /dev/null +++ b/patches/unapplied/server/0845-API-for-updating-recipes-on-clients.patch @@ -0,0 +1,114 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 21 Aug 2021 17:25:38 -0700 +Subject: [PATCH] API for updating recipes on clients + + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 2912b15ccda373cf52cec020b0e06ac2c5cf2950..a6caf3a0df22f124a4ee1cfb3981bbeb23a8630e 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -1490,6 +1490,13 @@ public abstract class PlayerList { + } + + public void reloadResources() { ++ // Paper start - API for updating recipes on clients ++ this.reloadAdvancementData(); ++ this.reloadTagData(); ++ this.reloadRecipeData(); ++ } ++ public void reloadAdvancementData() { ++ // Paper end - API for updating recipes on clients + // CraftBukkit start + /*Iterator iterator = this.advancements.values().iterator(); + +@@ -1505,7 +1512,15 @@ public abstract class PlayerList { + } + // CraftBukkit end + ++ // Paper start - API for updating recipes on clients ++ } ++ public void reloadTagData() { ++ // Paper end - API for updating recipes on clients + this.broadcastAll(new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(this.registries))); ++ // Paper start - API for updating recipes on clients ++ } ++ public void reloadRecipeData() { ++ // Paper end - API for updating recipes on clients + ClientboundUpdateRecipesPacket packetplayoutrecipeupdate = new ClientboundUpdateRecipesPacket(this.server.getRecipeManager().getOrderedRecipes()); + Iterator iterator1 = this.players.iterator(); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index bb89247a87067a74d793a1acc1eb95b98ace3d9e..75c222e592d676e98b293767d00de54a61411ae7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1176,6 +1176,18 @@ public final class CraftServer implements Server { + ReloadCommand.reload(this.console); + } + ++ // Paper start - API for updating recipes on clients ++ @Override ++ public void updateResources() { ++ this.playerList.reloadResources(); ++ } ++ ++ @Override ++ public void updateRecipes() { ++ this.playerList.reloadRecipeData(); ++ } ++ // Paper end - API for updating recipes on clients ++ + private void loadIcon() { + this.icon = new CraftIconCache(null); + try { +@@ -1555,6 +1567,13 @@ public final class CraftServer implements Server { + + @Override + public boolean addRecipe(Recipe recipe) { ++ // Paper start - API for updating recipes on clients ++ return this.addRecipe(recipe, false); ++ } ++ ++ @Override ++ public boolean addRecipe(Recipe recipe, boolean resendRecipes) { ++ // Paper end - API for updating recipes on clients + CraftRecipe toAdd; + if (recipe instanceof CraftRecipe) { + toAdd = (CraftRecipe) recipe; +@@ -1584,6 +1603,11 @@ public final class CraftServer implements Server { + } + } + toAdd.addToCraftingManager(); ++ // Paper start - API for updating recipes on clients ++ if (resendRecipes) { ++ this.playerList.reloadRecipeData(); ++ } ++ // Paper end - API for updating recipes on clients + return true; + } + +@@ -1764,10 +1788,23 @@ public final class CraftServer implements Server { + + @Override + public boolean removeRecipe(NamespacedKey recipeKey) { ++ // Paper start - API for updating recipes on clients ++ return this.removeRecipe(recipeKey, false); ++ } ++ ++ @Override ++ public boolean removeRecipe(NamespacedKey recipeKey, boolean resendRecipes) { ++ // Paper end - API for updating recipes on clients + Preconditions.checkArgument(recipeKey != null, "recipeKey == null"); + + ResourceLocation mcKey = CraftNamespacedKey.toMinecraft(recipeKey); +- return this.getServer().getRecipeManager().removeRecipe(mcKey); ++ // Paper start - resend recipes on successful removal ++ boolean removed = this.getServer().getRecipeManager().removeRecipe(mcKey); ++ if (removed && resendRecipes) { ++ this.playerList.reloadRecipeData(); ++ } ++ return removed; ++ // Paper end + } + + @Override diff --git a/patches/unapplied/server/0986-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch b/patches/unapplied/server/0986-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch new file mode 100644 index 0000000000..761c57c8f2 --- /dev/null +++ b/patches/unapplied/server/0986-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch @@ -0,0 +1,158 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Paul Sauve <[email protected]> +Date: Sat, 31 Oct 2020 18:43:02 -0500 +Subject: [PATCH] Strip raytracing for EntityLiving#hasLineOfSight + +The BlockGetter#clip method is very wasteful in both allocations, +and in logic. While EntityLiving#hasLineOfSight provides static +parameters for collisions with blocks and fluids, the method still does +a lot of dynamic checks for both of these, which result in extra work. +As well, since the fluid collision option is set to NONE, the entire +fluid collision system is completely unneeded, yet used anyways. + +Copyright (C) 2020 Technove LLC + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 5f1c585c819d25319a4fefd30abde3de7620cf6d..340c4452c09a98bc0220e6fe68dc65afc946986b 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -3774,7 +3774,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + Vec3 vec3d1 = new Vec3(entity.getX(), entity.getEyeY(), entity.getZ()); + + // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists +- return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared ++ return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clipDirect(vec3d, vec3d1, net.minecraft.world.phys.shapes.CollisionContext.of(this)) == HitResult.Type.MISS; // Paper - Perf: Use distance squared & strip raytracing + } + } + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index bb8e962e63c7a2d931f9bd7f7c002aa35cfa5fd3..0fa131a6c98adb498fc8d534e0e39647e80c6923 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -68,6 +68,18 @@ public interface BlockGetter extends LevelHeightAccessor { + }); + } + ++ // Paper start - Broken down variant of the method below, used by Level#clipDirect ++ @Nullable ++ default BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, BlockPos pos, BlockState state, net.minecraft.world.phys.shapes.CollisionContext collisionContext) { ++ if (state.isAir()) { ++ return null; ++ } ++ ++ final VoxelShape voxelshape = ClipContext.Block.COLLIDER.get(state, this, pos, collisionContext); ++ final BlockHitResult hitResult = this.clipWithInteractionOverride(start, end, pos, voxelshape, state); ++ return hitResult == null ? null : hitResult.getType(); ++ } ++ // Paper end + // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace + default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { + // Paper start - Add predicate for blocks when raytracing +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index c69ed3e899fc8d48afeb731bb3b2d97b5969e6e3..574175449af5b767f28e95ff8708ed37fedf4c7d 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -816,10 +816,87 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + return null; + } + +- // Paper start ++ // Paper start - Broken down method of raytracing for EntityLiving#hasLineOfSight, replaces BlockGetter#clip(CollisionContext) + public net.minecraft.world.phys.BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, net.minecraft.world.phys.shapes.CollisionContext context) { +- // To be patched over +- return this.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, context)).getType(); ++ // most of this code comes from BlockGetter#clip(CollisionContext, BiFunction, Function), but removes the needless functions ++ if (start.equals(end)) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ final double endX = Mth.lerp(-1.0E-7D, end.x, start.x); ++ final double endY = Mth.lerp(-1.0E-7D, end.y, start.y); ++ final double endZ = Mth.lerp(-1.0E-7D, end.z, start.z); ++ ++ final double startX = Mth.lerp(-1.0E-7D, start.x, end.x); ++ final double startY = Mth.lerp(-1.0E-7D, start.y, end.y); ++ final double startZ = Mth.lerp(-1.0E-7D, start.z, end.z); ++ ++ int currentX = Mth.floor(startX); ++ int currentY = Mth.floor(startY); ++ int currentZ = Mth.floor(startZ); ++ ++ final BlockPos.MutableBlockPos currentBlock = new BlockPos.MutableBlockPos(currentX, currentY, currentZ); ++ ++ LevelChunk chunk = this.getChunkIfLoaded(currentBlock); ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ final net.minecraft.world.phys.BlockHitResult.Type initialCheck = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context); ++ if (initialCheck != null) { ++ return initialCheck; ++ } ++ ++ final double diffX = endX - startX; ++ final double diffY = endY - startY; ++ final double diffZ = endZ - startZ; ++ ++ final int xDirection = Mth.sign(diffX); ++ final int yDirection = Mth.sign(diffY); ++ final int zDirection = Mth.sign(diffZ); ++ ++ final double normalizedX = xDirection == 0 ? Double.MAX_VALUE : (double) xDirection / diffX; ++ final double normalizedY = yDirection == 0 ? Double.MAX_VALUE : (double) yDirection / diffY; ++ final double normalizedZ = zDirection == 0 ? Double.MAX_VALUE : (double) zDirection / diffZ; ++ ++ double normalizedXDirection = normalizedX * (xDirection > 0 ? 1.0D - Mth.frac(startX) : Mth.frac(startX)); ++ double normalizedYDirection = normalizedY * (yDirection > 0 ? 1.0D - Mth.frac(startY) : Mth.frac(startY)); ++ double normalizedZDirection = normalizedZ * (zDirection > 0 ? 1.0D - Mth.frac(startZ) : Mth.frac(startZ)); ++ ++ net.minecraft.world.phys.BlockHitResult.Type result; ++ ++ do { ++ if (normalizedXDirection > 1.0D && normalizedYDirection > 1.0D && normalizedZDirection > 1.0D) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ ++ if (normalizedXDirection < normalizedYDirection) { ++ if (normalizedXDirection < normalizedZDirection) { ++ currentX += xDirection; ++ normalizedXDirection += normalizedX; ++ } else { ++ currentZ += zDirection; ++ normalizedZDirection += normalizedZ; ++ } ++ } else if (normalizedYDirection < normalizedZDirection) { ++ currentY += yDirection; ++ normalizedYDirection += normalizedY; ++ } else { ++ currentZ += zDirection; ++ normalizedZDirection += normalizedZ; ++ } ++ ++ currentBlock.set(currentX, currentY, currentZ); ++ if (chunk.getPos().x != currentBlock.getX() >> 4 || chunk.getPos().z != currentBlock.getZ() >> 4) { ++ chunk = this.getChunkIfLoaded(currentBlock); ++ if (chunk == null) { ++ return net.minecraft.world.phys.BlockHitResult.Type.MISS; ++ } ++ } ++ result = this.clipDirect(start, end, currentBlock, chunk.getBlockState(currentBlock), context); ++ } while (result == null); ++ ++ return result; + } + // Paper end + |