From 551f899db8c22bbb827dd0c02bee7263f9fefc80 Mon Sep 17 00:00:00 2001 From: TelepathicGrunt <40846040+TelepathicGrunt@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:10:14 -0400 Subject: [PATCH 1/9] Make RecipeWrapper implement RecipeInput (#1149) --- .../neoforge/items/wrapper/RecipeWrapper.java | 81 ++----------------- 1 file changed, 6 insertions(+), 75 deletions(-) diff --git a/src/main/java/net/neoforged/neoforge/items/wrapper/RecipeWrapper.java b/src/main/java/net/neoforged/neoforge/items/wrapper/RecipeWrapper.java index b35a713dde..b429a35914 100644 --- a/src/main/java/net/neoforged/neoforge/items/wrapper/RecipeWrapper.java +++ b/src/main/java/net/neoforged/neoforge/items/wrapper/RecipeWrapper.java @@ -5,15 +5,14 @@ package net.neoforged.neoforge.items.wrapper; -import net.minecraft.world.Container; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.items.IItemHandlerModifiable; +import net.minecraft.world.item.crafting.RecipeInput; +import net.neoforged.neoforge.items.IItemHandler; -public class RecipeWrapper implements Container { - protected final IItemHandlerModifiable inv; +public class RecipeWrapper implements RecipeInput { + protected final IItemHandler inv; - public RecipeWrapper(IItemHandlerModifiable inv) { + public RecipeWrapper(IItemHandler inv) { this.inv = inv; } @@ -21,7 +20,7 @@ public RecipeWrapper(IItemHandlerModifiable inv) { * Returns the size of this inventory. */ @Override - public int getContainerSize() { + public int size() { return inv.getSlots(); } @@ -32,72 +31,4 @@ public int getContainerSize() { public ItemStack getItem(int slot) { return inv.getStackInSlot(slot); } - - /** - * Attempts to remove n items from the specified slot. Returns the split stack that was removed. Modifies the inventory. - */ - @Override - public ItemStack removeItem(int slot, int count) { - ItemStack stack = inv.getStackInSlot(slot); - return stack.isEmpty() ? ItemStack.EMPTY : stack.split(count); - } - - /** - * Sets the contents of this slot to the provided stack. - */ - @Override - public void setItem(int slot, ItemStack stack) { - inv.setStackInSlot(slot, stack); - } - - /** - * Removes the stack contained in this slot from the underlying handler, and returns it. - */ - @Override - public ItemStack removeItemNoUpdate(int index) { - ItemStack s = getItem(index); - if (s.isEmpty()) return ItemStack.EMPTY; - setItem(index, ItemStack.EMPTY); - return s; - } - - @Override - public boolean isEmpty() { - for (int i = 0; i < inv.getSlots(); i++) { - if (!inv.getStackInSlot(i).isEmpty()) return false; - } - return true; - } - - @Override - public boolean canPlaceItem(int slot, ItemStack stack) { - return inv.isItemValid(slot, stack); - } - - @Override - public void clearContent() { - for (int i = 0; i < inv.getSlots(); i++) { - inv.setStackInSlot(i, ItemStack.EMPTY); - } - } - - //The following methods are never used by vanilla in crafting. They are defunct as mods need not override them. - @Override - public int getMaxStackSize() { - return 0; - } - - @Override - public void setChanged() {} - - @Override - public boolean stillValid(Player player) { - return false; - } - - @Override - public void startOpen(Player player) {} - - @Override - public void stopOpen(Player player) {} } From 36f959d713a0382fd444fd824b3222ff1d684cdc Mon Sep 17 00:00:00 2001 From: Brennan Ward Date: Sat, 22 Jun 2024 15:22:42 -0700 Subject: [PATCH 2/9] Update `ItemAttributeModifierEvent` to be based on `ItemAttributeModifiers` and update new vanilla attribute uses against the event (#1125) --- ...ItemAttributeModifiersPredicate.java.patch | 17 ++ .../net/minecraft/world/entity/Mob.java.patch | 11 + .../net/minecraft/world/item/Item.java.patch | 8 +- .../minecraft/world/item/ItemStack.java.patch | 59 +++-- .../neoforge/common/CommonHooks.java | 10 +- .../common/extensions/IItemExtension.java | 13 +- .../extensions/IItemStackExtension.java | 30 +-- .../event/ItemAttributeModifierEvent.java | 241 +++++++++++++----- .../neoforge/debug/item/ItemEventTests.java | 9 +- 9 files changed, 278 insertions(+), 120 deletions(-) create mode 100644 patches/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java.patch diff --git a/patches/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java.patch b/patches/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java.patch new file mode 100644 index 0000000000..c462257fef --- /dev/null +++ b/patches/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java ++++ b/net/minecraft/advancements/critereon/ItemAttributeModifiersPredicate.java +@@ -40,6 +_,14 @@ + return !this.modifiers.isPresent() || this.modifiers.get().test(p_341374_.modifiers()); + } + ++ /** ++ * Neo: Override this method to reflect gameplay attribute modifiers instead of only NBT modifiers. ++ */ ++ @Override ++ public boolean matches(ItemStack p_333958_) { ++ return matches(p_333958_, p_333958_.getAttributeModifiers()); ++ } ++ + public static record EntryPredicate( + Optional> attribute, + Optional id, diff --git a/patches/net/minecraft/world/entity/Mob.java.patch b/patches/net/minecraft/world/entity/Mob.java.patch index 8ea8d22b67..2bf2483fa8 100644 --- a/patches/net/minecraft/world/entity/Mob.java.patch +++ b/patches/net/minecraft/world/entity/Mob.java.patch @@ -69,6 +69,17 @@ Vec3i vec3i = this.getPickupReach(); for (ItemEntity itementity : this.level() +@@ -666,6 +_,10 @@ + + private double getApproximateAttackDamageWithItem(ItemStack p_330413_) { + ItemAttributeModifiers itemattributemodifiers = p_330413_.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); ++ ++ // Neo: Respect gameplay modifiers ++ itemattributemodifiers = p_330413_.getAttributeModifiers(); ++ + return itemattributemodifiers.compute(this.getAttributeBaseValue(Attributes.ATTACK_DAMAGE), EquipmentSlot.MAINHAND); + } + @@ -701,6 +_,7 @@ @Override diff --git a/patches/net/minecraft/world/item/Item.java.patch b/patches/net/minecraft/world/item/Item.java.patch index 481f3ce284..f6cdede428 100644 --- a/patches/net/minecraft/world/item/Item.java.patch +++ b/patches/net/minecraft/world/item/Item.java.patch @@ -118,12 +118,14 @@ public int getEnchantmentValue() { return 0; } -@@ -307,13 +_,20 @@ +@@ -307,13 +_,23 @@ return false; } -- @Deprecated -+ @Deprecated // Use ItemStack sensitive version or data components. (deprecated by vanilla too) ++ /** ++ * @deprecated Neo: Use {@link Item#getDefaultAttributeModifiers(ItemStack)} ++ */ + @Deprecated public ItemAttributeModifiers getDefaultAttributeModifiers() { return ItemAttributeModifiers.EMPTY; } diff --git a/patches/net/minecraft/world/item/ItemStack.java.patch b/patches/net/minecraft/world/item/ItemStack.java.patch index b211333782..d7f1a13aef 100644 --- a/patches/net/minecraft/world/item/ItemStack.java.patch +++ b/patches/net/minecraft/world/item/ItemStack.java.patch @@ -132,7 +132,7 @@ if (this.has(DataComponents.CUSTOM_NAME)) { mutablecomponent.withStyle(ChatFormatting.ITALIC); } -@@ -784,6 +_,7 @@ +@@ -784,12 +_,14 @@ list.add(DISABLED_ITEM_TOOLTIP); } @@ -140,6 +140,13 @@ return list; } } + + private void addAttributeTooltips(Consumer p_330796_, @Nullable Player p_330530_) { + ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); ++ // Neo: We don't need to call IItemStackExtension#getAttributeModifiers here, since it will be done in forEachModifier. + if (itemattributemodifiers.showInTooltip()) { + for (EquipmentSlotGroup equipmentslotgroup : EquipmentSlotGroup.values()) { + MutableBoolean mutableboolean = new MutableBoolean(true); @@ -897,6 +_,17 @@ return !this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty(); } @@ -158,27 +165,49 @@ public ItemEnchantments getEnchantments() { return this.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); } -@@ -933,11 +_,15 @@ +@@ -922,6 +_,12 @@ + } + + public void forEachModifier(EquipmentSlotGroup p_348610_, BiConsumer, AttributeModifier> p_348516_) { ++ // Neo: Reflect real attribute modifiers when doing iteration ++ this.getAttributeModifiers().forEach(p_348610_, p_348516_); ++ ++ if (false) { ++ // Start disabled vanilla code ++ + ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); + if (!itemattributemodifiers.modifiers().isEmpty()) { + itemattributemodifiers.forEach(p_348610_, p_348516_); +@@ -929,10 +_,19 @@ + this.getItem().getDefaultAttributeModifiers().forEach(p_348610_, p_348516_); + } + ++ // end disabled vanilla code ++ } ++ + EnchantmentHelper.forEachModifier(this, p_348610_, p_348516_); } public void forEachModifier(EquipmentSlot p_332001_, BiConsumer, AttributeModifier> p_330882_) { -- ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); -- if (!itemattributemodifiers.modifiers().isEmpty()) { -- itemattributemodifiers.forEach(p_332001_, p_330882_); -- } else { -- this.getItem().getDefaultAttributeModifiers().forEach(p_332001_, p_330882_); -+ // Neo: Use ItemStack extension method, which fires an event for mod-added attributes modifiers -+ this.getAttributeModifiers(p_332001_).forEach(p_330882_); ++ // Neo: Reflect real attribute modifiers when doing iteration ++ this.getAttributeModifiers().forEach(p_332001_, p_330882_); ++ + if (false) { -+ ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); -+ if (!itemattributemodifiers.modifiers().isEmpty()) { -+ itemattributemodifiers.forEach(p_332001_, p_330882_); -+ } else { -+ this.getItem().getDefaultAttributeModifiers().forEach(p_332001_, p_330882_); -+ } ++ // Start disabled vanilla code ++ + ItemAttributeModifiers itemattributemodifiers = this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); + if (!itemattributemodifiers.modifiers().isEmpty()) { + itemattributemodifiers.forEach(p_332001_, p_330882_); +@@ -940,6 +_,9 @@ + this.getItem().getDefaultAttributeModifiers().forEach(p_332001_, p_330882_); } ++ // end disabled vanilla code ++ } ++ EnchantmentHelper.forEachModifier(this, p_332001_, p_330882_); + } + @@ -951,7 +_,7 @@ MutableComponent mutablecomponent1 = ComponentUtils.wrapInSquareBrackets(mutablecomponent); diff --git a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java index d59c272a0e..874ce193eb 100644 --- a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java +++ b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java @@ -8,7 +8,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.mojang.serialization.Codec; import com.mojang.serialization.Lifecycle; @@ -88,8 +87,6 @@ import net.minecraft.world.entity.ExperienceOrb; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.SlotAccess; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.DefaultAttributes; import net.minecraft.world.entity.ai.village.poi.PoiManager; @@ -112,6 +109,7 @@ import net.minecraft.world.item.TippedArrowItem; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.alchemy.PotionContents; +import net.minecraft.world.item.component.ItemAttributeModifiers; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.ItemEnchantments; @@ -823,10 +821,10 @@ public static CriticalHitEvent fireCriticalHit(Player player, Entity target, boo /** * Hook to fire {@link ItemAttributeModifierEvent}. Modders should use {@link ItemStack#forEachModifier(EquipmentSlot, BiConsumer)} instead. */ - public static Multimap, AttributeModifier> getAttributeModifiers(ItemStack stack, EquipmentSlot equipmentSlot, Multimap, AttributeModifier> attributes) { - ItemAttributeModifierEvent event = new ItemAttributeModifierEvent(stack, equipmentSlot, attributes); + public static ItemAttributeModifiers computeModifiedAttributes(ItemStack stack, ItemAttributeModifiers defaultModifiers) { + ItemAttributeModifierEvent event = new ItemAttributeModifierEvent(stack, defaultModifiers); NeoForge.EVENT_BUS.post(event); - return event.getModifiers(); + return event.build(); } /** diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java index 197f541c45..574e660d0e 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IItemExtension.java @@ -5,7 +5,6 @@ package net.neoforged.neoforge.common.extensions; -import com.google.common.collect.Multimap; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -25,8 +24,6 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.animal.horse.Horse; import net.minecraft.world.entity.item.ItemEntity; @@ -67,20 +64,14 @@ private Item self() { /** * ItemStack sensitive version of getDefaultAttributeModifiers. Used when a stack has no {@link DataComponents#ATTRIBUTE_MODIFIERS} component. + * + * @see {@link IItemStackExtension#getAttributeModifiers()} for querying effective attribute modifiers. */ @SuppressWarnings("deprecation") default ItemAttributeModifiers getDefaultAttributeModifiers(ItemStack stack) { return self().getDefaultAttributeModifiers(); } - /** - * Called to allow items to modify the attributes on them. - * - * @param slot The slot to adjust the attributes for. - * @param modifiers Modifiers currently on the stack either because of a {@link DataComponents#ATTRIBUTE_MODIFIERS} component, or the default attribute modifiers - */ - default void adjustAttributeModifiers(ItemStack stack, EquipmentSlot slot, Multimap, AttributeModifier> modifiers) {} - /** * Called when a player drops the item into the world, returning false from this * will prevent the item from being removed from the players inventory and diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java index 72f0e770c2..46ca619634 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IItemStackExtension.java @@ -5,8 +5,6 @@ package net.neoforged.neoforge.common.extensions; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup.RegistryLookup; @@ -20,8 +18,6 @@ import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.animal.horse.Horse; import net.minecraft.world.entity.item.ItemEntity; @@ -477,21 +473,21 @@ default T getCapability(ItemCapability capability) { } /** - * {@return the attribute modifiers for the given equipment slot} + * Computes the gameplay attribute modifiers for this item stack. Used in place of direct access to {@link DataComponents.ATTRIBUTE_MODIFIERS} + * or {@link Item#getDefaultAttributeModifiers(ItemStack)} when querying attributes for gameplay purposes. *

- * Fires ItemAttributeModifierEvent to compute the final attribute modifiers. - * - * @param equipmentSlot the equipment slot to get the attribute modifiers for + * This method first computes the default modifiers, using {@link DataComponents.ATTRIBUTE_MODIFIERS} if present, otherwise + * falling back to {@link Item#getDefaultAttributeModifiers(ItemStack)}. + *

+ * The {@link ItemAttributeModifiersEvent} is then fired to allow external adjustments. */ - default Multimap, AttributeModifier> getAttributeModifiers(EquipmentSlot equipmentSlot) { - ItemAttributeModifiers itemattributemodifiers = self().getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); - Multimap, AttributeModifier> multimap = LinkedHashMultimap.create(); - if (!itemattributemodifiers.modifiers().isEmpty()) { - itemattributemodifiers.forEach(equipmentSlot, multimap::put); - } else { - self().getItem().getDefaultAttributeModifiers(self()).forEach(equipmentSlot, multimap::put); + default ItemAttributeModifiers getAttributeModifiers() { + ItemAttributeModifiers defaultModifiers = self().getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); + + if (defaultModifiers.modifiers().isEmpty()) { + defaultModifiers = self().getItem().getDefaultAttributeModifiers(self()); } - self().getItem().adjustAttributeModifiers(self(), equipmentSlot, multimap); - return CommonHooks.getAttributeModifiers(self(), equipmentSlot, multimap); + + return CommonHooks.computeModifiedAttributes(self(), defaultModifiers); } } diff --git a/src/main/java/net/neoforged/neoforge/event/ItemAttributeModifierEvent.java b/src/main/java/net/neoforged/neoforge/event/ItemAttributeModifierEvent.java index c374c2c293..bc489baddd 100644 --- a/src/main/java/net/neoforged/neoforge/event/ItemAttributeModifierEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/ItemAttributeModifierEvent.java @@ -5,118 +5,231 @@ package net.neoforged.neoforge.event; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import java.util.Collection; +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; import net.minecraft.core.Holder; -import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.core.component.DataComponents; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.ItemAttributeModifiers; import net.neoforged.bus.api.Event; -import net.neoforged.neoforge.common.NeoForge; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ApiStatus; /** - * This event is fired when the attributes for an ItemStack are being calculated. - * Attributes are calculated on the server when equipping and unequipping an item to add and remove attributes respectively, both must be consistent. - * Attributes are calculated on the client when rendering an item's tooltip to show relevant attributes. + * This event is fired when the attributes for an item stack are queried (for any reason) through {@link ItemStack#getAttributeModifiers()}. *
- * Note that this event is fired regardless of if the stack has NBT overriding attributes or not. If your attribute should be - * ignored when attributes are overridden, you can check for the presence of the {@code AttributeModifiers} tag. - *
- * This event is fired on the {@link NeoForge#EVENT_BUS}. + * This event is fired regardless of if the stack has {@link DataComponents#ATTRIBUTE_MODIFIERS} or not. If your attribute should be + * ignored when attributes are overridden, you can check for the presence of the component. + *

+ * This event may be fired on both the logical server and logical client. */ public class ItemAttributeModifierEvent extends Event { private final ItemStack stack; - private final EquipmentSlot slotType; - private final Multimap, AttributeModifier> originalModifiers; - private Multimap, AttributeModifier> unmodifiableModifiers; - @Nullable - private Multimap, AttributeModifier> modifiableModifiers; + private final ItemAttributeModifiers defaultModifiers; + private ItemAttributeModifiersBuilder builder; - public ItemAttributeModifierEvent(ItemStack stack, EquipmentSlot slotType, Multimap, AttributeModifier> modifiers) { + @ApiStatus.Internal + public ItemAttributeModifierEvent(ItemStack stack, ItemAttributeModifiers defaultModifiers) { this.stack = stack; - this.slotType = slotType; - this.unmodifiableModifiers = this.originalModifiers = modifiers; + this.defaultModifiers = defaultModifiers; } /** - * Returns an unmodifiable view of the attribute multimap. Use other methods from this event to modify the attributes map. - * Note that adding attributes based on existing attributes may lead to inconsistent results between the tooltip (client) - * and the actual attributes (server) if the listener order is different. Using {@link #getOriginalModifiers()} instead will give more consistent results. + * {@return the item stack whose attribute modifiers are being computed} */ - public Multimap, AttributeModifier> getModifiers() { - return this.unmodifiableModifiers; + public ItemStack getItemStack() { + return this.stack; } /** - * Returns the attribute map before any changes from other event listeners was made. + * {@return the default attribute modifiers before changes made by the event} */ - public Multimap, AttributeModifier> getOriginalModifiers() { - return this.originalModifiers; + public ItemAttributeModifiers getDefaultModifiers() { + return this.defaultModifiers; } /** - * Gets a modifiable map instance, creating it if the current map is currently unmodifiable + * Returns an unmodifiable view of the attribute modifier entries. Do not use the returned value to create an {@link ItemAttributeModifiers} + * since the underlying list is not immutable. + *

+ * If you need an {@link ItemAttributeModifiers}, you may need to call {@link #build()} + * + * @apiNote Use other methods from this event to adjust the modifiers. */ - private Multimap, AttributeModifier> getModifiableMap() { - if (this.modifiableModifiers == null) { - this.modifiableModifiers = HashMultimap.create(this.originalModifiers); - this.unmodifiableModifiers = Multimaps.unmodifiableMultimap(this.modifiableModifiers); - } - return this.modifiableModifiers; + public List getModifiers() { + return this.builder == null ? this.defaultModifiers.modifiers() : this.builder.getEntryView(); + } + + /** + * Adds a new attribute modifier to the given stack. Two modifiers with the same id may not exist for the same attribute, and this method will fail if one exists. + * + * @param attribute The attribute the modifier is for + * @param modifier The new attribute modifier + * @param slot The equipment slots for which the modifier should apply + * @return True if the modifier was added, false if it was already present + * @apiNote Modifiers must have a unique and consistent {@link ResourceLocation} id, or the modifier will not be removed when the item is unequipped. + */ + public boolean addModifier(Holder attribute, AttributeModifier modifier, EquipmentSlotGroup slot) { + return getBuilder().addModifier(attribute, modifier, slot); + } + + /** + * Removes an attribute modifier for the target attribute by id + * + * @return True if an attribute modifier was removed, false otherwise + */ + public boolean removeModifier(Holder attribute, ResourceLocation id) { + return getBuilder().removeModifier(attribute, id); } /** - * Adds a new attribute modifier to the given stack. - * Modifier must have a consistent UUID for consistency between equipping and unequipping items. - * Modifier name should clearly identify the mod that added the modifier. + * Adds a new attribute modifier to the given stack, optionally replacing any existing modifiers with the same id. * - * @param attribute Attribute - * @param modifier Modifier instance. - * @return True if the attribute was added, false if it was already present + * @param attribute The attribute the modifier is for + * @param modifier The new attribute modifier + * @param slot The equipment slots for which the modifier should apply + * @apiNote Modifiers must have a unique and consistent {@link ResourceLocation} id, or the modifier will not be removed when the item is unequipped. */ - public boolean addModifier(Holder attribute, AttributeModifier modifier) { - return getModifiableMap().put(attribute, modifier); + public void replaceModifier(Holder attribute, AttributeModifier modifier, EquipmentSlotGroup slot) { + removeModifier(attribute, modifier.id()); + addModifier(attribute, modifier, slot); } /** - * Removes a single modifier for the given attribute + * Removes modifiers based on a condition. * - * @param attribute Attribute - * @param modifier Modifier instance - * @return True if an attribute was removed, false if no change + * @return true if any modifiers were removed */ - public boolean removeModifier(Holder attribute, AttributeModifier modifier) { - return getModifiableMap().remove(attribute, modifier); + public boolean removeIf(Predicate condition) { + return getBuilder().removeIf(condition); } /** - * Removes all modifiers for the given attribute + * Removes all modifiers for the given attribute. * - * @param attribute Attribute - * @return Collection of removed modifiers + * @return true if any modifiers were removed */ - public Collection removeAttribute(Holder attribute) { - return getModifiableMap().removeAll(attribute); + public boolean removeAllModifiersFor(Holder attribute) { + return getBuilder().removeIf(entry -> entry.attribute().equals(attribute)); } /** - * Removes all modifiers for all attributes + * Removes all modifiers for all attributes. */ public void clearModifiers() { - getModifiableMap().clear(); + getBuilder().clear(); } - /** Gets the slot containing this stack */ - public EquipmentSlot getSlotType() { - return this.slotType; + /** + * Builds a new {@link ItemAttributeModifiers} with the results of this event, returning the + * {@linkplain #getDefaultModifiers() default modifiers} if no changes were made. + */ + public ItemAttributeModifiers build() { + return this.builder == null ? this.defaultModifiers : this.builder.build(this.defaultModifiers.showInTooltip()); } - /** Gets the item stack instance */ - public ItemStack getItemStack() { - return this.stack; + /** + * Returns the builder used for adjusting the attribute modifiers, creating it if it does not already exist. + */ + private ItemAttributeModifiersBuilder getBuilder() { + if (this.builder == null) { + this.builder = new ItemAttributeModifiersBuilder(this.defaultModifiers); + } + + return this.builder; + } + + /** + * Advanced version of {@link ItemAttributeModifiers.Builder} which supports removal and better sanity-checking. + *

+ * The original builder only supports additions and does not guarantee that no duplicate modifiers exist for a given id. + */ + private static class ItemAttributeModifiersBuilder { + private List entries; + private Map entriesByKey; + + ItemAttributeModifiersBuilder(ItemAttributeModifiers defaultModifiers) { + this.entries = new LinkedList<>(); + this.entriesByKey = new HashMap<>(defaultModifiers.modifiers().size()); + + for (ItemAttributeModifiers.Entry entry : defaultModifiers.modifiers()) { + entries.add(entry); + entriesByKey.put(new Key(entry.attribute(), entry.modifier().id()), entry); + } + } + + /** + * {@return an unmodifiable view of the underlying entry list} + */ + List getEntryView() { + return Collections.unmodifiableList(this.entries); + } + + /** + * Attempts to add a new modifier, refusing if one is already present with the same id. + * + * @return true if the modifier was added + */ + boolean addModifier(Holder attribute, AttributeModifier modifier, EquipmentSlotGroup slot) { + Key key = new Key(attribute, modifier.id()); + if (entriesByKey.containsKey(key)) { + return false; + } + + ItemAttributeModifiers.Entry entry = new ItemAttributeModifiers.Entry(attribute, modifier, slot); + entries.add(entry); + entriesByKey.put(key, entry); + return true; + } + + /** + * Removes a modifier for the target attribute with the given id. + * + * @return true if a modifier was removed + */ + boolean removeModifier(Holder attribute, ResourceLocation id) { + ItemAttributeModifiers.Entry entry = entriesByKey.remove(new Key(attribute, id)); + + if (entry != null) { + entries.remove(entry); + return true; + } + + return false; + } + + /** + * Removes modifiers based on a condition. + * + * @return true if any modifiers were removed + */ + boolean removeIf(Predicate condition) { + this.entries.removeIf(condition); + return this.entriesByKey.values().removeIf(condition); + } + + void clear() { + this.entries.clear(); + this.entriesByKey.clear(); + } + + public ItemAttributeModifiers build(boolean showInTooltip) { + return new ItemAttributeModifiers(ImmutableList.copyOf(this.entries), showInTooltip); + } + + /** + * Internal key class. Attribute modifiers are unique by id for each Attribute. + */ + private static record Key(Holder attr, ResourceLocation id) { + + } } } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemEventTests.java index 560fc5ebcb..4f033484cb 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/item/ItemEventTests.java @@ -8,6 +8,7 @@ import net.minecraft.gametest.framework.GameTest; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -26,11 +27,11 @@ public class ItemEventTests { @TestHolder(description = "Tests if the ItemAttributeModifierEvent allows modifying attributes") static void itemAttributeModifier(final DynamicTest test) { test.eventListeners().forge().addListener((final ItemAttributeModifierEvent event) -> { - if (event.getSlotType() == EquipmentSlot.MAINHAND && event.getItemStack().getItem() == Items.APPLE) { + if (event.getItemStack().getItem() == Items.APPLE) { ResourceLocation modifierId = ResourceLocation.fromNamespaceAndPath(test.createModId(), "apple_armor"); - event.addModifier(Attributes.ARMOR, new AttributeModifier(modifierId, 10f, AttributeModifier.Operation.ADD_VALUE)); - } else if (event.getSlotType() == EquipmentSlot.CHEST && event.getItemStack().is(Items.GOLDEN_CHESTPLATE)) { - event.clearModifiers(); + event.addModifier(Attributes.ARMOR, new AttributeModifier(modifierId, 10f, AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND); + } else if (event.getItemStack().is(Items.GOLDEN_CHESTPLATE)) { + event.removeIf(entry -> entry.slot().test(EquipmentSlot.CHEST)); } }); From 09ab1211e5a5ffa2dfacc9dfe1263d00d58b04b1 Mon Sep 17 00:00:00 2001 From: Dennis C Date: Sun, 23 Jun 2024 00:24:38 +0200 Subject: [PATCH 3/9] Cleanup access transformers (#1137) --- .../net/minecraft/client/gui/Gui.java.patch | 10 +- .../blockentity/PistonHeadRenderer.java.patch | 2 +- patches/net/minecraft/core/Holder.java.patch | 4 +- .../net/minecraft/core/HolderSet.java.patch | 2 +- .../world/level/block/BushBlock.java.patch | 2 +- .../neoforge/common/NeoForgeEventHandler.java | 2 +- .../server/command/ConfigCommand.java | 2 +- .../resources/META-INF/accesstransformer.cfg | 168 ++---------------- .../gametest/GameTestPlayer.java | 2 +- 9 files changed, 25 insertions(+), 169 deletions(-) diff --git a/patches/net/minecraft/client/gui/Gui.java.patch b/patches/net/minecraft/client/gui/Gui.java.patch index 2cefaca094..21dbe3ae1b 100644 --- a/patches/net/minecraft/client/gui/Gui.java.patch +++ b/patches/net/minecraft/client/gui/Gui.java.patch @@ -9,16 +9,16 @@ + @OnlyIn(Dist.CLIENT) public class Gui { - protected static final ResourceLocation CROSSHAIR_SPRITE = ResourceLocation.withDefaultNamespace("hud/crosshair"); + private static final ResourceLocation CROSSHAIR_SPRITE = ResourceLocation.withDefaultNamespace("hud/crosshair"); @@ -158,9 +_,21 @@ - protected long healthBlinkTime; - protected float autosaveIndicatorValue; - protected float lastAutosaveIndicatorValue; + private long healthBlinkTime; + private float autosaveIndicatorValue; + private float lastAutosaveIndicatorValue; + /** Neo: This is empty and unused, rendering goes through {@link #layerManager} instead. */ + @Deprecated private final LayeredDraw layers = new LayeredDraw(); + private final net.neoforged.neoforge.client.gui.GuiLayerManager layerManager = new net.neoforged.neoforge.client.gui.GuiLayerManager(); - protected float scopeScale; + private float scopeScale; + /** + * Neo: This variable controls the height of overlays on the left of the hotbar (e.g. health, armor). diff --git a/patches/net/minecraft/client/renderer/blockentity/PistonHeadRenderer.java.patch b/patches/net/minecraft/client/renderer/blockentity/PistonHeadRenderer.java.patch index f9d3c72db7..faecb45ea6 100644 --- a/patches/net/minecraft/client/renderer/blockentity/PistonHeadRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/blockentity/PistonHeadRenderer.java.patch @@ -5,7 +5,7 @@ BlockPos p_112459_, BlockState p_112460_, PoseStack p_112461_, MultiBufferSource p_112462_, Level p_112463_, boolean p_112464_, int p_112465_ ) { + if (true) { -+ net.neoforged.neoforge.client.ClientHooks.renderPistonMovedBlocks(p_112459_, p_112460_, p_112461_, p_112462_, p_112463_, p_112464_, p_112465_, blockRenderer == null ? blockRenderer = net.minecraft.client.Minecraft.getInstance().getBlockRenderer() : blockRenderer); ++ net.neoforged.neoforge.client.ClientHooks.renderPistonMovedBlocks(p_112459_, p_112460_, p_112461_, p_112462_, p_112463_, p_112464_, p_112465_, blockRenderer); + return; + } RenderType rendertype = ItemBlockRenderTypes.getMovingBlockRenderType(p_112460_); diff --git a/patches/net/minecraft/core/Holder.java.patch b/patches/net/minecraft/core/Holder.java.patch index 56c0ad8e75..f5d65b8c73 100644 --- a/patches/net/minecraft/core/Holder.java.patch +++ b/patches/net/minecraft/core/Holder.java.patch @@ -21,7 +21,7 @@ + return null; + } + - public void bindTags(Collection> p_205770_) { + void bindTags(Collection> p_205770_) { this.tags = Set.copyOf(p_205770_); } @@ -233,6 +_,28 @@ @@ -51,5 +51,5 @@ + + // Neo End - public static enum Type { + protected static enum Type { STAND_ALONE, diff --git a/patches/net/minecraft/core/HolderSet.java.patch b/patches/net/minecraft/core/HolderSet.java.patch index 8459624a17..a557fdb28d 100644 --- a/patches/net/minecraft/core/HolderSet.java.patch +++ b/patches/net/minecraft/core/HolderSet.java.patch @@ -11,7 +11,7 @@ int size(); @@ -174,6 +_,9 @@ - public void bind(List> p_205836_) { + void bind(List> p_205836_) { this.contents = List.copyOf(p_205836_); + for (Runnable runnable : this.invalidationCallbacks) { + runnable.run(); // FORGE: invalidate listeners when tags rebind diff --git a/patches/net/minecraft/world/level/block/BushBlock.java.patch b/patches/net/minecraft/world/level/block/BushBlock.java.patch index f0d823f947..4e200e841c 100644 --- a/patches/net/minecraft/world/level/block/BushBlock.java.patch +++ b/patches/net/minecraft/world/level/block/BushBlock.java.patch @@ -6,7 +6,7 @@ -public abstract class BushBlock extends Block { +public abstract class BushBlock extends Block implements net.neoforged.neoforge.common.IPlantable { - public BushBlock(BlockBehaviour.Properties p_51021_) { + protected BushBlock(BlockBehaviour.Properties p_51021_) { super(p_51021_); } @@ -33,6 +_,8 @@ diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeEventHandler.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeEventHandler.java index 4397530fb4..2e2e14ab5f 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeEventHandler.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeEventHandler.java @@ -120,7 +120,7 @@ public void onDpSync(final OnDatapackSyncEvent event) { // Note: don't send data maps over in-memory connections, else the client-side handling will wipe non-synced data maps. return; } - final var playerMaps = player.connection.connection.channel().attr(RegistryManager.ATTRIBUTE_KNOWN_DATA_MAPS).get(); + final var playerMaps = player.connection.getConnection().channel().attr(RegistryManager.ATTRIBUTE_KNOWN_DATA_MAPS).get(); if (playerMaps == null) return; // Skip gametest players for instance handleSync(player, regOpt.get(), playerMaps.getOrDefault(registry, List.of())); }); diff --git a/src/main/java/net/neoforged/neoforge/server/command/ConfigCommand.java b/src/main/java/net/neoforged/neoforge/server/command/ConfigCommand.java index 7c34b50a53..1dafaeaaed 100644 --- a/src/main/java/net/neoforged/neoforge/server/command/ConfigCommand.java +++ b/src/main/java/net/neoforged/neoforge/server/command/ConfigCommand.java @@ -64,7 +64,7 @@ private static int showFile(final CommandContext context) { // Click action not allowed on dedicated servers or connected LAN players as neither cannot click a link to a file on the server/LAN owner. // Only provide click action for single player world owners calling this command from in-game. ServerPlayer caller = context.getSource().getPlayer(); - if (FMLLoader.getDist().isClient() && caller != null && caller.connection.connection.isMemoryConnection()) { + if (FMLLoader.getDist().isClient() && caller != null && caller.connection.getConnection().isMemoryConnection()) { fileComponent.withStyle((style) -> style.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, f.getAbsolutePath()))); } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index e72297e555..d50f12c599 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -1,98 +1,7 @@ public net.minecraft.advancements.CriteriaTriggers register(Ljava/lang/String;Lnet/minecraft/advancements/CriterionTrigger;)Lnet/minecraft/advancements/CriterionTrigger; # register default net.minecraft.client.KeyMapping isDown # isDown -public net.minecraft.client.Minecraft textureManager # textureManager -public net.minecraft.client.Minecraft createSearchTrees()V # createSearchTrees public-f net.minecraft.client.Options keyMappings # keyMappings public net.minecraft.client.Options$FieldAccess -#group protected net.minecraft.client.gui.Gui * -protected net.minecraft.client.gui.Gui scopeScale # scopeScale -protected net.minecraft.client.gui.Gui SPYGLASS_SCOPE_LOCATION # SPYGLASS_SCOPE_LOCATION -protected net.minecraft.client.gui.Gui POWDER_SNOW_OUTLINE_LOCATION # POWDER_SNOW_OUTLINE_LOCATION -protected net.minecraft.client.gui.Gui COLOR_WHITE # COLOR_WHITE -protected net.minecraft.client.gui.Gui MIN_CROSSHAIR_ATTACK_SPEED # MIN_CROSSHAIR_ATTACK_SPEED -protected net.minecraft.client.gui.Gui NUM_HEARTS_PER_ROW # NUM_HEARTS_PER_ROW -protected net.minecraft.client.gui.Gui LINE_HEIGHT # LINE_HEIGHT -protected net.minecraft.client.gui.Gui SPACER # SPACER -protected net.minecraft.client.gui.Gui PORTAL_OVERLAY_ALPHA_MIN # PORTAL_OVERLAY_ALPHA_MIN -protected net.minecraft.client.gui.Gui HEART_SIZE # HEART_SIZE -protected net.minecraft.client.gui.Gui HEART_SEPARATION # HEART_SEPARATION -protected net.minecraft.client.gui.Gui autosaveIndicatorValue # autosaveIndicatorValue -protected net.minecraft.client.gui.Gui lastAutosaveIndicatorValue # lastAutosaveIndicatorValue -protected net.minecraft.client.gui.Gui SAVING_TEXT # SAVING_TEXT -protected net.minecraft.client.gui.Gui AUTOSAVE_FADE_SPEED_FACTOR # AUTOSAVE_FADE_SPEED_FACTOR -protected net.minecraft.client.gui.Gui chatDisabledByPlayerShown # chatDisabledByPlayerShown -protected net.minecraft.client.gui.Gui ARMOR_HALF_SPRITE # ARMOR_HALF_SPRITE -protected net.minecraft.client.gui.Gui EXPERIENCE_BAR_BACKGROUND_SPRITE # EXPERIENCE_BAR_BACKGROUND_SPRITE -protected net.minecraft.client.gui.Gui EFFECT_BACKGROUND_AMBIENT_SPRITE # EFFECT_BACKGROUND_AMBIENT_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_SPRITE # HOTBAR_SPRITE -protected net.minecraft.client.gui.Gui JUMP_BAR_PROGRESS_SPRITE # JUMP_BAR_PROGRESS_SPRITE -protected net.minecraft.client.gui.Gui FOOD_HALF_HUNGER_SPRITE # FOOD_HALF_HUNGER_SPRITE -protected net.minecraft.client.gui.Gui CROSSHAIR_SPRITE # CROSSHAIR_SPRITE -protected net.minecraft.client.gui.Gui FOOD_FULL_HUNGER_SPRITE # FOOD_FULL_HUNGER_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_ATTACK_INDICATOR_PROGRESS_SPRITE # HOTBAR_ATTACK_INDICATOR_PROGRESS_SPRITE -protected net.minecraft.client.gui.Gui FOOD_EMPTY_SPRITE # FOOD_EMPTY_SPRITE -protected net.minecraft.client.gui.Gui ARMOR_FULL_SPRITE # ARMOR_FULL_SPRITE -protected net.minecraft.client.gui.Gui AIR_BURSTING_SPRITE # AIR_BURSTING_SPRITE -protected net.minecraft.client.gui.Gui FOOD_EMPTY_HUNGER_SPRITE # FOOD_EMPTY_HUNGER_SPRITE -protected net.minecraft.client.gui.Gui CROSSHAIR_ATTACK_INDICATOR_PROGRESS_SPRITE # CROSSHAIR_ATTACK_INDICATOR_PROGRESS_SPRITE -protected net.minecraft.client.gui.Gui CROSSHAIR_ATTACK_INDICATOR_FULL_SPRITE # CROSSHAIR_ATTACK_INDICATOR_FULL_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_SELECTION_SPRITE # HOTBAR_SELECTION_SPRITE -protected net.minecraft.client.gui.Gui HEART_VEHICLE_FULL_SPRITE # HEART_VEHICLE_FULL_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_OFFHAND_RIGHT_SPRITE # HOTBAR_OFFHAND_RIGHT_SPRITE -protected net.minecraft.client.gui.Gui EFFECT_BACKGROUND_SPRITE # EFFECT_BACKGROUND_SPRITE -protected net.minecraft.client.gui.Gui HEART_VEHICLE_HALF_SPRITE # HEART_VEHICLE_HALF_SPRITE -protected net.minecraft.client.gui.Gui ARMOR_EMPTY_SPRITE # ARMOR_EMPTY_SPRITE -protected net.minecraft.client.gui.Gui FOOD_FULL_SPRITE # FOOD_FULL_SPRITE -protected net.minecraft.client.gui.Gui JUMP_BAR_COOLDOWN_SPRITE # JUMP_BAR_COOLDOWN_SPRITE -protected net.minecraft.client.gui.Gui EXPERIENCE_BAR_PROGRESS_SPRITE # EXPERIENCE_BAR_PROGRESS_SPRITE -protected net.minecraft.client.gui.Gui CROSSHAIR_ATTACK_INDICATOR_BACKGROUND_SPRITE # CROSSHAIR_ATTACK_INDICATOR_BACKGROUND_SPRITE -protected net.minecraft.client.gui.Gui FOOD_HALF_SPRITE # FOOD_HALF_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_OFFHAND_LEFT_SPRITE # HOTBAR_OFFHAND_LEFT_SPRITE -protected net.minecraft.client.gui.Gui AIR_SPRITE # AIR_SPRITE -protected net.minecraft.client.gui.Gui HEART_VEHICLE_CONTAINER_SPRITE # HEART_VEHICLE_CONTAINER_SPRITE -protected net.minecraft.client.gui.Gui JUMP_BAR_BACKGROUND_SPRITE # JUMP_BAR_BACKGROUND_SPRITE -protected net.minecraft.client.gui.Gui HOTBAR_ATTACK_INDICATOR_BACKGROUND_SPRITE # HOTBAR_ATTACK_INDICATOR_BACKGROUND_SPRITE -protected net.minecraft.client.gui.Gui debugOverlay # debugOverlay -protected net.minecraft.client.gui.Gui titleFadeInTime # titleFadeInTime -protected net.minecraft.client.gui.Gui titleStayTime # titleStayTime -protected net.minecraft.client.gui.Gui titleFadeOutTime # titleFadeOutTime -protected net.minecraft.client.gui.Gui lastHealth # lastHealth -protected net.minecraft.client.gui.Gui displayHealth # displayHealth -protected net.minecraft.client.gui.Gui lastHealthTime # lastHealthTime -protected net.minecraft.client.gui.Gui healthBlinkTime # healthBlinkTime -protected net.minecraft.client.gui.Gui screenWidth # screenWidth -protected net.minecraft.client.gui.Gui screenHeight # screenHeight -protected net.minecraft.client.gui.Gui VIGNETTE_LOCATION # VIGNETTE_LOCATION -protected net.minecraft.client.gui.Gui PUMPKIN_BLUR_LOCATION # PUMPKIN_BLUR_LOCATION -protected net.minecraft.client.gui.Gui DEMO_EXPIRED_TEXT # DEMO_EXPIRED_TEXT -protected net.minecraft.client.gui.Gui random # random -protected net.minecraft.client.gui.Gui minecraft # minecraft -protected net.minecraft.client.gui.Gui itemRenderer # itemRenderer -protected net.minecraft.client.gui.Gui chat # chat -protected net.minecraft.client.gui.Gui tickCount # tickCount -protected net.minecraft.client.gui.Gui overlayMessageString # overlayMessageString -protected net.minecraft.client.gui.Gui overlayMessageTime # overlayMessageTime -protected net.minecraft.client.gui.Gui animateOverlayMessageColor # animateOverlayMessageColor -protected net.minecraft.client.gui.Gui toolHighlightTimer # toolHighlightTimer -protected net.minecraft.client.gui.Gui lastToolHighlight # lastToolHighlight -protected net.minecraft.client.gui.Gui subtitleOverlay # subtitleOverlay -protected net.minecraft.client.gui.Gui spectatorGui # spectatorGui -protected net.minecraft.client.gui.Gui tabList # tabList -protected net.minecraft.client.gui.Gui bossOverlay # bossOverlay -protected net.minecraft.client.gui.Gui titleTime # titleTime -protected net.minecraft.client.gui.Gui title # title -protected net.minecraft.client.gui.Gui subtitle # subtitle -#endgroup -protected net.minecraft.client.gui.Gui renderHearts(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/entity/player/Player;IIIIFIIIZ)V # renderHearts -public net.minecraft.client.gui.Gui displayScoreboardSidebar(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/scores/Objective;)V # displayScoreboardSidebar -public net.minecraft.client.gui.Gui renderCrosshair(Lnet/minecraft/client/gui/GuiGraphics;)V # renderCrosshair -public net.minecraft.client.gui.Gui renderVignette(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/entity/Entity;)V # renderVignette -protected net.minecraft.client.gui.Gui renderTextureOverlay(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/resources/ResourceLocation;F)V # renderTextureOverlay -public net.minecraft.client.gui.Gui renderSpyglassOverlay(Lnet/minecraft/client/gui/GuiGraphics;F)V # renderSpyglassOverlay -protected net.minecraft.client.gui.Gui renderPortalOverlay(Lnet/minecraft/client/gui/GuiGraphics;F)V # renderPortalOverlay -public net.minecraft.client.gui.Gui renderHotbar(FLnet/minecraft/client/gui/GuiGraphics;)V # renderHotbar -public net.minecraft.client.gui.Gui renderEffects(Lnet/minecraft/client/gui/GuiGraphics;)V # renderEffects -protected net.minecraft.client.gui.Gui drawBackdrop(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/gui/Font;III)V # drawBackdrop public net.minecraft.client.gui.Gui$HeartType protected net.minecraft.client.gui.components.AbstractButton SPRITES protected net.minecraft.client.gui.components.AbstractSelectionList$Entry list # list @@ -100,24 +9,18 @@ protected net.minecraft.client.gui.components.AbstractSliderButton getSprite()Ln protected net.minecraft.client.gui.components.AbstractSliderButton getHandleSprite()Lnet/minecraft/resources/ResourceLocation; # getHandleSprite public net.minecraft.client.gui.components.AbstractWidget renderScrollingString(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Component;IIIII)V public net.minecraft.client.gui.components.AbstractWidget renderScrollingString(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Component;IIIIII)V -protected net.minecraft.client.gui.components.DebugScreenOverlay renderProfilerChart # renderProfilerChart -protected net.minecraft.client.gui.components.DebugScreenOverlay renderNetworkCharts # renderNetworkCharts -protected net.minecraft.client.gui.components.DebugScreenOverlay renderFpsCharts # renderFpsCharts -protected net.minecraft.client.gui.components.DebugScreenOverlay block # block -protected net.minecraft.client.gui.components.DebugScreenOverlay liquid # liquid public net.minecraft.client.gui.screens.MenuScreens$ScreenConstructor public net.minecraft.client.gui.screens.Screen renderables # renderables public net.minecraft.client.model.geom.LayerDefinitions OUTER_ARMOR_DEFORMATION # OUTER_ARMOR_DEFORMATION public net.minecraft.client.model.geom.LayerDefinitions INNER_ARMOR_DEFORMATION # INNER_ARMOR_DEFORMATION -public net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl connection # connection public net.minecraft.client.multiplayer.ClientPacketListener commands # commands public net.minecraft.client.multiplayer.SessionSearchTrees CREATIVE_NAMES public net.minecraft.client.multiplayer.SessionSearchTrees CREATIVE_TAGS public net.minecraft.client.multiplayer.SessionSearchTrees$Key -public net.minecraft.client.particle.FireworkParticles$Starter createParticle(DDDDDD[I[IZZ)V # createParticle -public net.minecraft.client.particle.FireworkParticles$Starter createParticleBall(DI[I[IZZ)V # createParticleBall -public net.minecraft.client.particle.FireworkParticles$Starter createParticleShape(D[[D[I[IZZZ)V # createParticleShape -public net.minecraft.client.particle.FireworkParticles$Starter createParticleBurst([I[IZZ)V # createParticleBurst +public net.minecraft.client.particle.FireworkParticles$Starter createParticle(DDDDDDLit/unimi/dsi/fastutil/ints/IntList;Lit/unimi/dsi/fastutil/ints/IntList;ZZ)V # createParticle +public net.minecraft.client.particle.FireworkParticles$Starter createParticleBall(DILit/unimi/dsi/fastutil/ints/IntList;Lit/unimi/dsi/fastutil/ints/IntList;ZZ)V # createParticleBall +public net.minecraft.client.particle.FireworkParticles$Starter createParticleShape(D[[DLit/unimi/dsi/fastutil/ints/IntList;Lit/unimi/dsi/fastutil/ints/IntList;ZZZ)V # createParticleShape +public net.minecraft.client.particle.FireworkParticles$Starter createParticleBurst(Lit/unimi/dsi/fastutil/ints/IntList;Lit/unimi/dsi/fastutil/ints/IntList;ZZ)V # createParticleBurst public net.minecraft.client.particle.ParticleEngine register(Lnet/minecraft/core/particles/ParticleType;Lnet/minecraft/client/particle/ParticleEngine$SpriteParticleRegistration;)V # register public net.minecraft.client.particle.ParticleEngine register(Lnet/minecraft/core/particles/ParticleType;Lnet/minecraft/client/particle/ParticleProvider;)V # register public net.minecraft.client.particle.ParticleEngine register(Lnet/minecraft/core/particles/ParticleType;Lnet/minecraft/client/particle/ParticleProvider$Sprite;)V # register @@ -177,19 +80,14 @@ public net.minecraft.client.renderer.block.model.ItemTransform$Deserializer DEFA public net.minecraft.client.renderer.block.model.ItemTransform$Deserializer DEFAULT_SCALE # DEFAULT_SCALE public net.minecraft.client.renderer.block.model.ItemTransforms$Deserializer public net.minecraft.client.renderer.block.model.ItemTransforms$Deserializer ()V # constructor -public net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher fontRenderer # fontRenderer - needed for rendering text in TESR items before entering world public net.minecraft.client.renderer.blockentity.BlockEntityRenderers register(Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/client/renderer/blockentity/BlockEntityRendererProvider;)V # register -private-f net.minecraft.client.renderer.blockentity.PistonHeadRenderer blockRenderer # blockRenderer - it's static so we need to un-finalize in case this class loads to early. public net.minecraft.client.renderer.blockentity.SkullBlockRenderer SKIN_BY_TYPE # SKIN_BY_TYPE -public net.minecraft.client.renderer.entity.EntityRenderDispatcher renderers # renderers public net.minecraft.client.renderer.entity.EntityRenderers register(Lnet/minecraft/world/entity/EntityType;Lnet/minecraft/client/renderer/entity/EntityRendererProvider;)V # register -protected net.minecraft.client.renderer.entity.ItemEntityRenderer getRenderAmount(Lnet/minecraft/world/item/ItemStack;)I # getRenderAmount public net.minecraft.client.renderer.entity.ItemRenderer renderQuadList(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Ljava/util/List;Lnet/minecraft/world/item/ItemStack;II)V # renderQuadList public net.minecraft.client.renderer.entity.ItemRenderer renderModelLists(Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/item/ItemStack;IILcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;)V # renderModelLists public net.minecraft.client.renderer.entity.LivingEntityRenderer addLayer(Lnet/minecraft/client/renderer/entity/layers/RenderLayer;)Z # addLayer public net.minecraft.client.renderer.texture.SpriteContents byMipLevel # byMipLevel default net.minecraft.client.renderer.texture.SpriteContents animatedTexture # animatedTexture -default net.minecraft.client.renderer.texture.SpriteContents getFrameCount()I # getFrameCount public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations (Ljava/util/List;Lnet/minecraft/resources/ResourceLocation;Ljava/util/Map;)V # constructor public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations$PalettedSpriteSupplier public net.minecraft.client.renderer.texture.atlas.sources.PalettedPermutations$PalettedSpriteSupplier (Lnet/minecraft/client/renderer/texture/atlas/sources/LazyLoadedImage;Ljava/util/function/Supplier;Lnet/minecraft/resources/ResourceLocation;)V # constructor @@ -199,25 +97,15 @@ public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInst public net.minecraft.client.renderer.texture.atlas.sources.Unstitcher$RegionInstance (Lnet/minecraft/client/renderer/texture/atlas/sources/LazyLoadedImage;Lnet/minecraft/client/renderer/texture/atlas/sources/Unstitcher$Region;DD)V # constructor public net.minecraft.client.resources.ClientPackSource createVanillaPackSource(Ljava/nio/file/Path;)Lnet/minecraft/server/packs/VanillaPackResources; # createVanillaPackSource protected net.minecraft.client.resources.TextureAtlasHolder textureAtlas # textureAtlas -protected net.minecraft.client.resources.model.ModelBakery loadBlockModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/block/model/BlockModel; # loadBlockModel public net.minecraft.client.resources.model.SimpleBakedModel$Builder (ZZZLnet/minecraft/client/renderer/block/model/ItemTransforms;Lnet/minecraft/client/renderer/block/model/ItemOverrides;)V # constructor public net.minecraft.client.sounds.SoundEngine soundManager # soundManager public net.minecraft.commands.CommandSourceStack source # source public net.minecraft.commands.arguments.selector.EntitySelectorParser finalizePredicates()V # finalizePredicates public net.minecraft.commands.arguments.selector.EntitySelectorParser parseOptions()V # parseOptions public net.minecraft.commands.arguments.selector.options.EntitySelectorOptions register(Ljava/lang/String;Lnet/minecraft/commands/arguments/selector/options/EntitySelectorOptions$Modifier;Ljava/util/function/Predicate;Lnet/minecraft/network/chat/Component;)V # register -public net.minecraft.core.Holder$Reference bindTags(Ljava/util/Collection;)V # bindTags -public net.minecraft.core.Holder$Reference bindKey(Lnet/minecraft/resources/ResourceKey;)V # bindKey -public net.minecraft.core.Holder$Reference bindValue(Ljava/lang/Object;)V # bindValue -public net.minecraft.core.Holder$Reference$Type -public net.minecraft.core.HolderSet$Named bind(Ljava/util/List;)V # bind protected net.minecraft.core.IdMapper nextId # nextId protected net.minecraft.core.IdMapper tToId # tToId - internal map protected net.minecraft.core.IdMapper idToT # idToT - internal index list -protected net.minecraft.core.MappedRegistry unregisteredIntrusiveHolders # unregisteredIntrusiveHolders -public net.minecraft.core.RegistrySetBuilder$LazyHolder bindValue(Ljava/lang/Object;)V # bindValue -public net.minecraft.core.RegistrySynchronization$NetworkedRegistryData -public net.minecraft.core.particles.ParticleType (ZLnet/minecraft/core/particles/ParticleOptions$Deserializer;)V # constructor public net.minecraft.core.particles.SimpleParticleType (Z)V # constructor protected net.minecraft.data.loot.BlockLootSubProvider createSilkTouchOnlyTable(Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/world/level/storage/loot/LootTable$Builder; # createSilkTouchOnlyTable protected net.minecraft.data.loot.BlockLootSubProvider createPotFlowerItemTable(Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/world/level/storage/loot/LootTable$Builder; # createPotFlowerItemTable @@ -232,7 +120,6 @@ protected net.minecraft.data.recipes.RecipeProvider getBaseBlock(Lnet/minecraft/ protected net.minecraft.data.recipes.RecipeProvider buttonBuilder(Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/RecipeBuilder; # buttonBuilder protected net.minecraft.data.recipes.RecipeProvider fenceBuilder(Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/RecipeBuilder; # fenceBuilder protected net.minecraft.data.recipes.RecipeProvider fenceGateBuilder(Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/RecipeBuilder; # fenceGateBuilder -protected net.minecraft.data.recipes.RecipeProvider trapdoorBuilder(Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/RecipeBuilder; # trapdoorBuilder protected net.minecraft.data.recipes.RecipeProvider signBuilder(Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/RecipeBuilder; # signBuilder protected net.minecraft.data.recipes.RecipeProvider smeltingResultFromBase(Lnet/minecraft/data/recipes/RecipeOutput;Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/level/ItemLike;)V # smeltingResultFromBase protected net.minecraft.data.recipes.RecipeProvider cutBuilder(Lnet/minecraft/data/recipes/RecipeCategory;Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/item/crafting/Ingredient;)Lnet/minecraft/data/recipes/ShapedRecipeBuilder; # cutBuilder @@ -243,7 +130,6 @@ protected net.minecraft.data.recipes.RecipeProvider pressurePlateBuilder(Lnet/mi protected net.minecraft.data.recipes.RecipeProvider nineBlockStorageRecipes(Lnet/minecraft/data/recipes/RecipeOutput;Lnet/minecraft/data/recipes/RecipeCategory;Lnet/minecraft/world/level/ItemLike;Lnet/minecraft/data/recipes/RecipeCategory;Lnet/minecraft/world/level/ItemLike;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V # nineBlockStorageRecipes protected net.minecraft.data.recipes.RecipeProvider simpleCookingRecipe(Lnet/minecraft/data/recipes/RecipeOutput;Ljava/lang/String;Lnet/minecraft/world/item/crafting/RecipeSerializer;Lnet/minecraft/world/item/crafting/AbstractCookingRecipe$Factory;ILnet/minecraft/world/level/ItemLike;Lnet/minecraft/world/level/ItemLike;F)V # simpleCookingRecipe protected net.minecraft.data.recipes.RecipeProvider inventoryTrigger([Lnet/minecraft/advancements/critereon/ItemPredicate$Builder;)Lnet/minecraft/advancements/Criterion; # inventoryTrigger -public net.minecraft.data.recipes.ShapedRecipeBuilder$Result public net.minecraft.data.recipes.packs.VanillaRecipeProvider COAL_SMELTABLES # COAL_SMELTABLES public net.minecraft.data.recipes.packs.VanillaRecipeProvider IRON_SMELTABLES # IRON_SMELTABLES public net.minecraft.data.recipes.packs.VanillaRecipeProvider COPPER_SMELTABLES # COPPER_SMELTABLES @@ -257,35 +143,24 @@ public net.minecraft.data.tags.IntrinsicHolderTagsProvider$IntrinsicTagAppender protected net.minecraft.data.tags.TagsProvider builders # builders public-f net.minecraft.data.tags.TagsProvider getName()Ljava/lang/String; # getName public net.minecraft.data.tags.TagsProvider$TagAppender -public net.minecraft.gametest.framework.GameTestServer (Ljava/lang/Thread;Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;Lnet/minecraft/server/packs/repository/PackRepository;Lnet/minecraft/server/WorldStem;Ljava/util/Collection;Lnet/minecraft/core/BlockPos;)V # constructor public net.minecraft.gametest.framework.GameTestHelper testInfo # testInfo public net.minecraft.gametest.framework.GameTestInfo sequences # sequences public net.minecraft.gametest.framework.GameTestSequence (Lnet/minecraft/gametest/framework/GameTestInfo;)V # protected net.minecraft.resources.RegistryOps (Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/resources/RegistryOps$RegistryInfoLookup;)V # constructor public net.minecraft.resources.ResourceLocation validNamespaceChar(C)Z # validNamespaceChar -public net.minecraft.resources.ResourceLocation isValidPath(Ljava/lang/String;)Z # isValidPath -public net.minecraft.resources.ResourceLocation isValidNamespace(Ljava/lang/String;)Z # isValidNamespace protected net.minecraft.server.MinecraftServer nextTickTimeNanos # nextTickTimeNanos public net.minecraft.server.MinecraftServer$ReloadableResources -public net.minecraft.server.dedicated.DedicatedServer consoleInput # consoleInput public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder; public net.minecraft.server.level.ServerChunkCache level # level public net.minecraft.server.level.ServerLevel getEntities()Lnet/minecraft/world/level/entity/LevelEntityGetter; # getEntities -public net.minecraft.server.level.ServerPlayer containerCounter # containerCounter -public net.minecraft.server.level.ServerPlayer initMenu(Lnet/minecraft/world/inventory/AbstractContainerMenu;)V # initMenu -public net.minecraft.server.level.ServerPlayer nextContainerCounter()V # nextContainerCounter -public net.minecraft.server.network.ServerCommonPacketListenerImpl connection # connection -public net.minecraft.server.network.ServerLoginPacketListenerImpl authenticatedProfile # authenticatedProfile public net.minecraft.server.packs.FilePackResources (Lnet/minecraft/server/packs/PackLocationInfo;Lnet/minecraft/server/packs/FilePackResources$SharedZipFileAccess;Ljava/lang/String;)V # constructor public net.minecraft.server.packs.FilePackResources$SharedZipFileAccess public net.minecraft.server.packs.FilePackResources$SharedZipFileAccess (Ljava/io/File;)V # constructor public net.minecraft.server.packs.OverlayMetadataSection$OverlayEntry CODEC # CODEC public net.minecraft.server.packs.repository.BuiltInPackSource fixedResources(Lnet/minecraft/server/packs/PackResources;)Lnet/minecraft/server/packs/repository/Pack$ResourcesSupplier; # fixedResources -public net.minecraft.server.packs.repository.ServerPacksSource createVanillaPackSource()Lnet/minecraft/server/packs/VanillaPackResources; # createVanillaPackSource public net.minecraft.server.packs.repository.Pack getDeclaredPackVersions(Ljava/lang/String;Lnet/minecraft/server/packs/metadata/pack/PackMetadataSection;)Lnet/minecraft/util/InclusiveRange; # getDeclaredPackVersions public net.minecraft.server.packs.repository.PackRepository rebuildSelected(Ljava/util/Collection;)Ljava/util/List; # rebuildSelected public net.minecraft.server.packs.resources.FallbackResourceManager fallbacks # fallbacks -public net.minecraft.util.ExtraCodecs$EitherCodec public net.minecraft.util.datafix.fixes.StructuresBecomeConfiguredFix$Conversion public net.minecraft.util.thread.BlockableEventLoop submitAsync(Ljava/lang/Runnable;)Ljava/util/concurrent/CompletableFuture; # submitAsync public net.minecraft.world.damagesource.CombatTracker INTENTIONAL_GAME_DESIGN_STYLE # INTENTIONAL_GAME_DESIGN_STYLE @@ -303,8 +178,6 @@ public net.minecraft.world.entity.Entity getEncodeId()Ljava/lang/String; # getEn public net.minecraft.world.entity.ExperienceOrb value # value public net.minecraft.world.entity.Mob goalSelector # goalSelector public net.minecraft.world.entity.Mob targetSelector # targetSelector -public net.minecraft.world.entity.SpawnPlacements register(Lnet/minecraft/world/entity/EntityType;Lnet/minecraft/world/entity/SpawnPlacements$Type;Lnet/minecraft/world/level/levelgen/Heightmap$Types;Lnet/minecraft/world/entity/SpawnPlacements$SpawnPredicate;)V # register -public net.minecraft.world.entity.ai.memory.MemoryModuleType (Ljava/util/Optional;)V # constructor public net.minecraft.world.entity.ai.sensing.SensorType (Ljava/util/function/Supplier;)V # constructor protected net.minecraft.world.entity.item.PrimedTnt explode()V # explode - make it easier to extend TNTEntity with custom explosion logic protected net.minecraft.world.entity.monster.AbstractSkeleton getStepSound()Lnet/minecraft/sounds/SoundEvent; # getStepSound - make AbstractSkeletonEntity implementable @@ -321,7 +194,6 @@ protected net.minecraft.world.entity.monster.Zombie conversionTime public net.minecraft.world.inventory.AnvilMenu repairItemCountCost # repairItemCountCost public net.minecraft.world.inventory.MenuType (Lnet/minecraft/world/inventory/MenuType$MenuSupplier;Lnet/minecraft/world/flag/FeatureFlagSet;)V # constructor public net.minecraft.world.inventory.MenuType$MenuSupplier -public net.minecraft.world.item.CreativeModeTab$Output public net.minecraft.world.item.CreativeModeTab$TabVisibility public net.minecraft.world.item.CreativeModeTabs COLORED_BLOCKS # COLORED_BLOCKS public net.minecraft.world.item.CreativeModeTabs SPAWN_EGGS # SPAWN_EGGS @@ -338,19 +210,11 @@ public net.minecraft.world.item.CreativeModeTabs INGREDIENTS # INGREDIENTS public net.minecraft.world.item.CreativeModeTabs REDSTONE_BLOCKS # REDSTONE_BLOCKS public net.minecraft.world.item.CreativeModeTabs INVENTORY # INVENTORY #group public net.minecraft.world.item.Item -public net.minecraft.world.item.AxeItem (Lnet/minecraft/world/item/Tier;FFLnet/minecraft/world/item/Item$Properties;)V # constructor -public net.minecraft.world.item.DiggerItem (FFLnet/minecraft/world/item/Tier;Lnet/minecraft/tags/TagKey;Lnet/minecraft/world/item/Item$Properties;)V # constructor -public net.minecraft.world.item.HoeItem (Lnet/minecraft/world/item/Tier;IFLnet/minecraft/world/item/Item$Properties;)V # constructor -public net.minecraft.world.item.PickaxeItem (Lnet/minecraft/world/item/Tier;IFLnet/minecraft/world/item/Item$Properties;)V # constructor -public net.minecraft.world.item.RecordItem (ILnet/minecraft/sounds/SoundEvent;Lnet/minecraft/world/item/Item$Properties;I)V # constructor +public net.minecraft.world.item.DiggerItem (Lnet/minecraft/world/item/Tier;Lnet/minecraft/tags/TagKey;Lnet/minecraft/world/item/Item$Properties;)V # constructor #endgroup private-f net.minecraft.world.item.Item components -public net.minecraft.world.item.ItemStack (Lnet/minecraft/world/level/ItemLike;ILjava/util/Optional;)V # constructor public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG # TYPE_AND_TAG public net.minecraft.world.item.alchemy.PotionBrewing$Mix -public net.minecraft.world.item.alchemy.PotionBrewing$Mix to # to -public net.minecraft.world.item.alchemy.PotionBrewing$Mix from # from -public net.minecraft.world.item.alchemy.PotionBrewing$Mix ingredient # ingredient public net.minecraft.world.item.context.BlockPlaceContext (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/BlockHitResult;)V # constructor public net.minecraft.world.item.context.UseOnContext (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/BlockHitResult;)V # constructor public net.minecraft.world.item.crafting.Ingredient fromValues(Ljava/util/stream/Stream;)Lnet/minecraft/world/item/crafting/Ingredient; # fromValues @@ -390,7 +254,6 @@ protected net.minecraft.world.level.biome.MobSpawnSettings$Builder spawners # sp protected net.minecraft.world.level.biome.MobSpawnSettings$Builder mobSpawnCosts # mobSpawnCosts protected net.minecraft.world.level.biome.MobSpawnSettings$Builder creatureGenerationProbability # creatureGenerationProbability #group public net.minecraft.world.level.block.Block -public net.minecraft.world.level.block.AirBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.AttachedStemBlock (Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.AzaleaBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.BarrierBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor @@ -401,7 +264,6 @@ public net.minecraft.world.level.block.BaseCoralWallFanBlock (Lnet/minecra public net.minecraft.world.level.block.BigDripleafBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.BigDripleafStemBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.BlastFurnaceBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor -public net.minecraft.world.level.block.BushBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.ButtonBlock (Lnet/minecraft/world/level/block/state/properties/BlockSetType;ILnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.CactusBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.CakeBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor @@ -416,7 +278,6 @@ public net.minecraft.world.level.block.CoralPlantBlock (Lnet/minecraft/wor public net.minecraft.world.level.block.CoralWallFanBlock (Lnet/minecraft/world/level/block/Block;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.CraftingTableBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.CropBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor -public net.minecraft.world.level.block.CrossCollisionBlock (FFFFFLnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.DeadBushBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.DecoratedPotBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.DirtPathBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor @@ -428,7 +289,6 @@ public net.minecraft.world.level.block.EndPortalBlock (Lnet/minecraft/worl public net.minecraft.world.level.block.EndRodBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.EnderChestBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.EquipableCarvedPumpkinBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor -public net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.FarmBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.FletchingTableBlock (Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor public net.minecraft.world.level.block.FungusBlock (Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/block/Block;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V # constructor @@ -502,7 +362,6 @@ public net.minecraft.world.level.block.entity.HopperBlockEntity isOnCustomCooldo public net.minecraft.world.level.block.state.BlockBehaviour propertiesCodec()Lcom/mojang/serialization/codecs/RecordCodecBuilder; public net.minecraft.world.level.block.state.properties.BlockSetType register(Lnet/minecraft/world/level/block/state/properties/BlockSetType;)Lnet/minecraft/world/level/block/state/properties/BlockSetType; # register public net.minecraft.world.level.block.state.properties.WoodType register(Lnet/minecraft/world/level/block/state/properties/WoodType;)Lnet/minecraft/world/level/block/state/properties/WoodType; # register -public net.minecraft.world.level.chunk.ChunkStatus (Lnet/minecraft/world/level/chunk/ChunkStatus;IZLjava/util/EnumSet;Lnet/minecraft/world/level/chunk/ChunkStatus$ChunkType;Lnet/minecraft/world/level/chunk/ChunkStatus$GenerationTask;Lnet/minecraft/world/level/chunk/ChunkStatus$LoadingTask;)V # constructor protected net.minecraft.world.level.levelgen.Aquifer$NoiseBasedAquifer barrierNoise # barrierNoise protected net.minecraft.world.level.levelgen.Aquifer$NoiseBasedAquifer lavaNoise # lavaNoise protected net.minecraft.world.level.levelgen.Aquifer$NoiseBasedAquifer aquiferCache # aquiferCache @@ -520,7 +379,7 @@ protected net.minecraft.world.level.levelgen.Aquifer$NoiseBasedAquifer gridY(I)I protected net.minecraft.world.level.levelgen.Aquifer$NoiseBasedAquifer gridZ(I)I # gridZ protected net.minecraft.world.level.levelgen.Beardifier pieceIterator # pieceIterator protected net.minecraft.world.level.levelgen.Beardifier junctionIterator # junctionIterator -protected net.minecraft.world.level.levelgen.Beardifier getBuryContribution(III)D # getBuryContribution +protected net.minecraft.world.level.levelgen.Beardifier getBuryContribution(DDD)D # getBuryContribution protected net.minecraft.world.level.levelgen.Beardifier getBeardContribution(IIII)D # getBeardContribution private-f net.minecraft.world.level.levelgen.DebugLevelSource ALL_BLOCKS # ALL_BLOCKS private-f net.minecraft.world.level.levelgen.DebugLevelSource GRID_WIDTH # GRID_WIDTH @@ -535,21 +394,18 @@ public net.minecraft.world.level.levelgen.NoiseGeneratorSettings floatingIslands public net.minecraft.world.level.levelgen.NoiseGeneratorSettings nether(Lnet/minecraft/data/worldgen/BootstrapContext;)Lnet/minecraft/world/level/levelgen/NoiseGeneratorSettings; # nether public net.minecraft.world.level.levelgen.NoiseGeneratorSettings lambda$static$0(Lcom/mojang/serialization/codecs/RecordCodecBuilder$Instance;)Lcom/mojang/datafixers/kinds/App; # lambda$static$0 #endgroup -public net.minecraft.world.level.levelgen.feature.featuresize.FeatureSizeType (Lcom/mojang/serialization/Codec;)V # constructor -public net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerType (Lcom/mojang/serialization/Codec;)V # constructor -public net.minecraft.world.level.levelgen.feature.rootplacers.RootPlacerType (Lcom/mojang/serialization/Codec;)V # constructor -public net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProviderType (Lcom/mojang/serialization/Codec;)V # constructor -public net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType (Lcom/mojang/serialization/Codec;)V # constructor -public net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType (Lcom/mojang/serialization/Codec;)V # constructor +public net.minecraft.world.level.levelgen.feature.featuresize.FeatureSizeType (Lcom/mojang/serialization/MapCodec;)V # constructor +public net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerType (Lcom/mojang/serialization/MapCodec;)V # constructor +public net.minecraft.world.level.levelgen.feature.rootplacers.RootPlacerType (Lcom/mojang/serialization/MapCodec;)V # constructor +public net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProviderType (Lcom/mojang/serialization/MapCodec;)V # constructor +public net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType (Lcom/mojang/serialization/MapCodec;)V # constructor +public net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType (Lcom/mojang/serialization/MapCodec;)V # constructor protected net.minecraft.world.level.portal.PortalForcer level # level public net.minecraft.world.level.storage.LevelResource (Ljava/lang/String;)V # constructor private-f net.minecraft.world.level.storage.loot.LootPool rolls # rolls private-f net.minecraft.world.level.storage.loot.LootPool bonusRolls # bonusRolls -public net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket KNOWN_TYPES -public net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket KNOWN_TYPES public net.minecraft.server.network.ServerConfigurationPacketListenerImpl finishCurrentTask(Lnet/minecraft/server/network/ConfigurationTask$Type;)V public com.mojang.blaze3d.vertex.VertexFormatElement$Usage$SetupState -public com.mojang.blaze3d.vertex.VertexFormatElement$Usage$ClearState public net.minecraft.world.level.block.LiquidBlock fluid public net.minecraft.world.item.BucketItem content public net.minecraft.world.item.ItemStack addToTooltip(Lnet/minecraft/core/component/DataComponentType;Lnet/minecraft/world/item/Item$TooltipContext;Ljava/util/function/Consumer;Lnet/minecraft/world/item/TooltipFlag;)V diff --git a/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java b/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java index 814658dc3e..3d8f853544 100644 --- a/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java +++ b/testframework/src/main/java/net/neoforged/testframework/gametest/GameTestPlayer.java @@ -99,7 +99,7 @@ private void disconnectGameTest() { @SuppressWarnings("unchecked") private Stream> outboundPackets() { - return ((EmbeddedChannel) connection.connection.channel()).outboundMessages().stream() + return ((EmbeddedChannel) connection.getConnection().channel()).outboundMessages().stream() .filter(Packet.class::isInstance).map(obj -> (Packet) obj) .flatMap((Function, Stream>>) packet -> { if (!(packet instanceof ClientboundBundlePacket clientboundBundlePacket)) return Stream.of(packet); From 4c040617f6c94384ad7878f2c4965eeee0b1ac67 Mon Sep 17 00:00:00 2001 From: Dennis C Date: Sun, 23 Jun 2024 00:26:31 +0200 Subject: [PATCH 4/9] Add missing alias resolve in MappedRegistry#getHolder() and properly handle null name in MappedRegistry#get() (#1130) --- patches/net/minecraft/core/MappedRegistry.java.patch | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/patches/net/minecraft/core/MappedRegistry.java.patch b/patches/net/minecraft/core/MappedRegistry.java.patch index aaf171b661..e7f390bb75 100644 --- a/patches/net/minecraft/core/MappedRegistry.java.patch +++ b/patches/net/minecraft/core/MappedRegistry.java.patch @@ -69,7 +69,13 @@ } @Nullable -@@ -189,7 +_,7 @@ +@@ -184,12 +_,12 @@ + + @Override + public Optional> getHolder(ResourceLocation p_316743_) { +- return Optional.ofNullable(this.byLocation.get(p_316743_)); ++ return Optional.ofNullable(this.byLocation.get(resolve(p_316743_))); + } @Override public Optional> getHolder(ResourceKey p_205905_) { @@ -92,7 +98,7 @@ @Override public T get(@Nullable ResourceLocation p_122739_) { - Holder.Reference reference = this.byLocation.get(p_122739_); -+ Holder.Reference reference = this.byLocation.get(resolve(p_122739_)); ++ Holder.Reference reference = this.byLocation.get(p_122739_ != null ? resolve(p_122739_) : null); return getValueFromNullable(reference); } From 1bd3d1980d252f8bc3c50ee4e60ce68ebc0ed7aa Mon Sep 17 00:00:00 2001 From: Brennan Ward Date: Sat, 22 Jun 2024 15:31:42 -0700 Subject: [PATCH 5/9] Add missing tool, block entity, and breaker context to getExpDrop (#1157) --- .../block/DropExperienceBlock.java.patch | 10 +++++---- .../level/block/RedStoneOreBlock.java.patch | 10 ++++++--- .../level/block/SculkCatalystBlock.java.patch | 9 +++++--- .../level/block/SculkSensorBlock.java.patch | 8 ++++--- .../level/block/SculkShriekerBlock.java.patch | 8 ++++--- .../world/level/block/SpawnerBlock.java.patch | 11 ++++++---- .../common/extensions/IBlockExtension.java | 17 ++++++++------- .../extensions/IBlockStateExtension.java | 21 ++++++++++--------- .../neoforge/event/level/BlockDropsEvent.java | 4 ++-- 9 files changed, 59 insertions(+), 39 deletions(-) diff --git a/patches/net/minecraft/world/level/block/DropExperienceBlock.java.patch b/patches/net/minecraft/world/level/block/DropExperienceBlock.java.patch index 48e428511a..0145bd6e3a 100644 --- a/patches/net/minecraft/world/level/block/DropExperienceBlock.java.patch +++ b/patches/net/minecraft/world/level/block/DropExperienceBlock.java.patch @@ -1,17 +1,19 @@ --- a/net/minecraft/world/level/block/DropExperienceBlock.java +++ b/net/minecraft/world/level/block/DropExperienceBlock.java -@@ -30,8 +_,11 @@ +@@ -30,8 +_,13 @@ @Override protected void spawnAfterBreak(BlockState p_221086_, ServerLevel p_221087_, BlockPos p_221088_, ItemStack p_221089_, boolean p_221090_) { super.spawnAfterBreak(p_221086_, p_221087_, p_221088_, p_221089_, p_221090_); - if (p_221090_) { - this.tryDropExperience(p_221087_, p_221088_, p_221089_, this.xpRange); - } -+ + } + ++ // Neo: Patch-in override for getExpDrop. Original vanilla logic passes this.xpRange to tryDropExperience. + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader level, net.minecraft.util.RandomSource randomSource, BlockPos pos) { -+ return this.xpRange.sample(randomSource); ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { ++ return this.xpRange.sample(level.getRandom()); } } diff --git a/patches/net/minecraft/world/level/block/RedStoneOreBlock.java.patch b/patches/net/minecraft/world/level/block/RedStoneOreBlock.java.patch index 8825312536..cb1fabced6 100644 --- a/patches/net/minecraft/world/level/block/RedStoneOreBlock.java.patch +++ b/patches/net/minecraft/world/level/block/RedStoneOreBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/RedStoneOreBlock.java +++ b/net/minecraft/world/level/block/RedStoneOreBlock.java -@@ -87,9 +_,10 @@ +@@ -87,9 +_,14 @@ @Override protected void spawnAfterBreak(BlockState p_221907_, ServerLevel p_221908_, BlockPos p_221909_, ItemStack p_221910_, boolean p_221911_) { super.spawnAfterBreak(p_221907_, p_221908_, p_221909_, p_221910_, p_221911_); @@ -8,9 +8,13 @@ - this.tryDropExperience(p_221908_, p_221909_, p_221910_, UniformInt.of(1, 5)); - } + } ++ ++ // Neo: Patch-in override for getExpDrop. Original vanilla logic passes UniformInt.of(1, 5) to tryDropExperience. + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader world, RandomSource randomSource, BlockPos pos) { -+ return 1 + randomSource.nextInt(5); ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { ++ return UniformInt.of(1, 5).sample(level.getRandom()); } @Override diff --git a/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch b/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch index b666cbacfd..6fb3c95fbd 100644 --- a/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkCatalystBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SculkCatalystBlock.java +++ b/net/minecraft/world/level/block/SculkCatalystBlock.java -@@ -66,8 +_,11 @@ +@@ -66,8 +_,14 @@ @Override protected void spawnAfterBreak(BlockState p_222109_, ServerLevel p_222110_, BlockPos p_222111_, ItemStack p_222112_, boolean p_222113_) { super.spawnAfterBreak(p_222109_, p_222110_, p_222111_, p_222112_, p_222113_); @@ -10,8 +10,11 @@ + + } + ++ // Neo: Patch-in override for getExpDrop. Original vanilla logic passes this.xpRange to tryDropExperience. + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader level, RandomSource randomSource, BlockPos pos) { -+ return this.xpRange.sample(randomSource); ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { ++ return this.xpRange.sample(level.getRandom()); } } diff --git a/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch b/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch index f589a5b6f7..de183fc8b2 100644 --- a/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkSensorBlock.java.patch @@ -1,17 +1,19 @@ --- a/net/minecraft/world/level/block/SculkSensorBlock.java +++ b/net/minecraft/world/level/block/SculkSensorBlock.java -@@ -292,8 +_,11 @@ +@@ -292,8 +_,13 @@ @Override protected void spawnAfterBreak(BlockState p_222142_, ServerLevel p_222143_, BlockPos p_222144_, ItemStack p_222145_, boolean p_222146_) { super.spawnAfterBreak(p_222142_, p_222143_, p_222144_, p_222145_, p_222146_); - if (p_222146_) { - this.tryDropExperience(p_222143_, p_222144_, p_222145_, ConstantInt.of(5)); - } -+ + } + ++ // Neo: Patch-in override for getExpDrop. Original vanilla logic passes ConstantInt.of(5) to tryDropExperience. + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader level, RandomSource randomSource, BlockPos pos) { ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { + return 5; } } diff --git a/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch b/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch index 5eaafd724f..6f5dedbc34 100644 --- a/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SculkShriekerBlock.java.patch @@ -1,17 +1,19 @@ --- a/net/minecraft/world/level/block/SculkShriekerBlock.java +++ b/net/minecraft/world/level/block/SculkShriekerBlock.java -@@ -141,9 +_,12 @@ +@@ -141,9 +_,14 @@ @Override protected void spawnAfterBreak(BlockState p_222192_, ServerLevel p_222193_, BlockPos p_222194_, ItemStack p_222195_, boolean p_222196_) { super.spawnAfterBreak(p_222192_, p_222193_, p_222194_, p_222195_, p_222196_); - if (p_222196_) { - this.tryDropExperience(p_222193_, p_222194_, p_222195_, ConstantInt.of(5)); - } -+ + } + ++ // Neo: Patch-in override for getExpDrop. Original vanilla logic passes ConstantInt.of(5) to tryDropExperience. + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader level, RandomSource randomSource, BlockPos pos) { ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { + return 5; } diff --git a/patches/net/minecraft/world/level/block/SpawnerBlock.java.patch b/patches/net/minecraft/world/level/block/SpawnerBlock.java.patch index 829c09b809..506bb8805c 100644 --- a/patches/net/minecraft/world/level/block/SpawnerBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SpawnerBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/SpawnerBlock.java +++ b/net/minecraft/world/level/block/SpawnerBlock.java -@@ -46,10 +_,12 @@ +@@ -46,10 +_,15 @@ @Override protected void spawnAfterBreak(BlockState p_222477_, ServerLevel p_222478_, BlockPos p_222479_, ItemStack p_222480_, boolean p_222481_) { super.spawnAfterBreak(p_222477_, p_222478_, p_222479_, p_222480_, p_222481_); @@ -8,12 +8,15 @@ - int i = 15 + p_222478_.random.nextInt(15) + p_222478_.random.nextInt(15); - this.popExperience(p_222478_, p_222479_, i); - } -+ + } + ++ // Neo: Patch-in override for getExpDrop. Also fixes MC-273642 (Spawner XP drops bypass enchantments) ++ // Original vanilla logic passes 15 + p_222478_.random.nextInt(15) + p_222478_.random.nextInt(15) to popExperience, bypassing enchantments + @Override -+ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelReader world, net.minecraft.util.RandomSource randomSource, BlockPos pos) { -+ return 15 + randomSource.nextInt(15) + randomSource.nextInt(15); ++ public int getExpDrop(BlockState state, net.minecraft.world.level.LevelAccessor level, BlockPos pos, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.level.block.entity.BlockEntity blockEntity, ++ @org.jetbrains.annotations.Nullable net.minecraft.world.entity.Entity breaker, ItemStack tool) { ++ return 15 + level.getRandom().nextInt(15) + level.getRandom().nextInt(15); } @Override diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index 8e194a3823..e424c0b868 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -30,6 +30,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.ShovelItem; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.EmptyBlockGetter; @@ -445,15 +446,17 @@ default boolean isPortalFrame(BlockState state, BlockGetter level, BlockPos pos) } /** - * Gathers how much experience this block drops when broken. + * Returns how many experience points this block drops when broken, before application of {@linkplain EnchantmentEffectComponents#BLOCK_EXPERIENCE enchantments}. * - * @param state The current state - * @param level The level - * @param randomSource Random source to use for experience randomness - * @param pos Block position - * @return Amount of XP from breaking this block. + * @param state The state of the block being broken + * @param level The level + * @param pos The position of the block being broken + * @param blockEntity The block entity, if any + * @param breaker The entity who broke the block, if known + * @param tool The item stack used to break the block. May be empty + * @return The amount of experience points dropped by this block */ - default int getExpDrop(BlockState state, LevelReader level, RandomSource randomSource, BlockPos pos) { + default int getExpDrop(BlockState state, LevelAccessor level, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity breaker, ItemStack tool) { return 0; } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index 352e2123ac..032f96a3f1 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -22,6 +22,7 @@ import net.minecraft.world.entity.projectile.FishingHook; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Explosion; @@ -31,6 +32,7 @@ import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration; @@ -338,18 +340,17 @@ default boolean isPortalFrame(BlockGetter level, BlockPos pos) { } /** - * Gathers how much experience this block drops when broken. + * Returns how many experience points this block drops when broken, before application of {@linkplain EnchantmentEffectComponents#BLOCK_EXPERIENCE enchantments}. * - * @param level The level - * @param randomSource Random source to use for experience randomness - * @param pos Block position - * @param fortuneLevel fortune enchantment level of tool being used - * @param silkTouchLevel silk touch enchantment level of tool being used - * @return Amount of XP from breaking this block. + * @param level The level + * @param pos The position of the block being broken + * @param blockEntity The block entity, if any + * @param breaker The entity who broke the block, if known + * @param tool The item stack used to break the block. May be empty + * @return The amount of experience points dropped by this block */ - default int getExpDrop(LevelReader level, RandomSource randomSource, BlockPos pos) { - // TODO: Change this method to have context of the full tool ItemStack instead of just the enchantment levels. - return self().getBlock().getExpDrop(self(), level, randomSource, pos); + default int getExpDrop(LevelAccessor level, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity breaker, ItemStack tool) { + return self().getBlock().getExpDrop(self(), level, pos, blockEntity, breaker, tool); } default BlockState rotate(LevelAccessor level, BlockPos pos, Rotation direction) { diff --git a/src/main/java/net/neoforged/neoforge/event/level/BlockDropsEvent.java b/src/main/java/net/neoforged/neoforge/event/level/BlockDropsEvent.java index 595211caeb..609f65f471 100644 --- a/src/main/java/net/neoforged/neoforge/event/level/BlockDropsEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/level/BlockDropsEvent.java @@ -54,7 +54,7 @@ public BlockDropsEvent(ServerLevel level, BlockPos pos, BlockState state, @Nulla this.breaker = breaker; this.tool = tool; - this.experience = EnchantmentHelper.processBlockExperience(level, tool, state.getExpDrop(level, level.random, pos)); + this.experience = EnchantmentHelper.processBlockExperience(level, tool, state.getExpDrop(level, pos, blockEntity, breaker, tool)); } /** @@ -115,7 +115,7 @@ public int getDroppedExperience() { } /** - * Set the amount of experience points that will be dropped by the block + * Set the amount of experience points that will be dropped by the block. This is the true value, after enchantments have been applied. * * @param experience The new amount. Must not be negative. * @apiNote When cancelled, no experience is dropped, regardless of this value. From 64818684995e54157125afca4e7707c35e09ccba Mon Sep 17 00:00:00 2001 From: Dennis C Date: Sun, 23 Jun 2024 00:36:25 +0200 Subject: [PATCH 6/9] Fix duplicate registration error for empty loot table (#1151) --- .../net/neoforged/neoforge/event/EventHooks.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/neoforged/neoforge/event/EventHooks.java b/src/main/java/net/neoforged/neoforge/event/EventHooks.java index 36a3023286..94909f0ab2 100644 --- a/src/main/java/net/neoforged/neoforge/event/EventHooks.java +++ b/src/main/java/net/neoforged/neoforge/event/EventHooks.java @@ -8,6 +8,7 @@ import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; import com.mojang.datafixers.util.Either; +import com.mojang.serialization.DynamicOps; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenCustomHashSet; import java.io.File; import java.util.EnumSet; @@ -100,6 +101,7 @@ import net.minecraft.world.level.portal.PortalShape; import net.minecraft.world.level.storage.PlayerDataStorage; import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.world.level.storage.loot.LootDataType; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; @@ -680,12 +682,18 @@ public static boolean onProjectileImpact(Projectile projectile, HitResult ray) { return NeoForge.EVENT_BUS.post(new ProjectileImpactEvent(projectile, ray)).isCanceled(); } + /** + * Fires the {@link LootTableLoadEvent} for non-empty loot tables and returns the table if the event was not + * canceled and the table was not set to {@link LootTable#EMPTY} in the event. Otherwise returns {@code null} + * which maps to an empty {@link Optional} in {@link LootDataType#deserialize(ResourceLocation, DynamicOps, Object)} + */ + @Nullable public static LootTable loadLootTable(ResourceLocation name, LootTable table) { if (table == LootTable.EMPTY) // Empty table has a null name, and shouldn't be modified anyway. - return table; + return null; LootTableLoadEvent event = new LootTableLoadEvent(name, table); - if (NeoForge.EVENT_BUS.post(event).isCanceled()) - return LootTable.EMPTY; + if (NeoForge.EVENT_BUS.post(event).isCanceled() || event.getTable() == LootTable.EMPTY) + return null; return event.getTable(); } From 412c6ab06e5457de8f08e15e03cc07266306786a Mon Sep 17 00:00:00 2001 From: dhyces <10985914+dhyces@users.noreply.github.com> Date: Sun, 23 Jun 2024 00:45:20 -0700 Subject: [PATCH 7/9] Fix attachment codec wrappers not using registry ops (#1165) --- .../neoforge/attachment/AttachmentType.java | 4 +-- .../debug/attachment/AttachmentTests.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/neoforged/neoforge/attachment/AttachmentType.java b/src/main/java/net/neoforged/neoforge/attachment/AttachmentType.java index 27a1023571..f7994a8cc7 100644 --- a/src/main/java/net/neoforged/neoforge/attachment/AttachmentType.java +++ b/src/main/java/net/neoforged/neoforge/attachment/AttachmentType.java @@ -202,13 +202,13 @@ public Builder serialize(Codec codec, Predicate shouldSerialize return serialize(new IAttachmentSerializer<>() { @Override public T read(IAttachmentHolder holder, Tag tag, HolderLookup.Provider provider) { - return codec.parse(NbtOps.INSTANCE, tag).result().get(); + return codec.parse(provider.createSerializationContext(NbtOps.INSTANCE), tag).result().get(); } @Nullable @Override public Tag write(T attachment, HolderLookup.Provider provider) { - return shouldSerialize.test(attachment) ? codec.encodeStart(NbtOps.INSTANCE, attachment).result().get() : null; + return shouldSerialize.test(attachment) ? codec.encodeStart(provider.createSerializationContext(NbtOps.INSTANCE), attachment).result().get() : null; } }); } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/attachment/AttachmentTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/attachment/AttachmentTests.java index 9dca900afc..d14d6753b2 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/attachment/AttachmentTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/attachment/AttachmentTests.java @@ -12,10 +12,16 @@ import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.Registries; import net.minecraft.gametest.framework.GameTest; import net.minecraft.nbt.IntTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.level.GameType; import net.minecraft.world.level.chunk.LevelChunk; import net.neoforged.neoforge.attachment.AttachmentType; @@ -127,4 +133,24 @@ static void playerAttachmentCopyOnRespawn(DynamicTest test, RegistrationHelper r helper.succeed(); }); } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests that attachments with dynamic data are de/serialized well") + static void dynamicDataContentSerialization(DynamicTest test, RegistrationHelper reg) { + var stackType = reg.attachments() + .register("stack", () -> AttachmentType.builder(() -> new ItemStack(Items.IRON_AXE)).serialize(ItemStack.CODEC).build()); + test.onGameTest(helper -> { + var player = helper.makeMockPlayer(); + var stack = new ItemStack(Items.IRON_SWORD); + var enchantments = new ItemEnchantments.Mutable(ItemEnchantments.EMPTY); + enchantments.set(helper.getLevel().registryAccess().registryOrThrow(Registries.ENCHANTMENT).getHolderOrThrow(Enchantments.SHARPNESS), 3); + stack.set(DataComponents.ENCHANTMENTS, enchantments.toImmutable()); + player.setData(stackType, stack); + helper.catchException(() -> { + player.serializeAttachments(helper.getLevel().registryAccess()); // This will throw if it fails + }); + helper.succeed(); + }); + } } From 003f3e45fa0be5f546fe30e235b91dd487a2279e Mon Sep 17 00:00:00 2001 From: Caltinor <62700786+Caltinor@users.noreply.github.com> Date: Mon, 24 Jun 2024 04:29:11 -0400 Subject: [PATCH 8/9] Damage Pipeline Update (#792) --- .../client/player/LocalPlayer.java.patch | 8 - .../client/player/RemotePlayer.java.patch | 10 -- .../minecraft/world/entity/Entity.java.patch | 13 ++ .../world/entity/LivingEntity.java.patch | 141 ++++++++++++--- .../world/entity/player/Player.java.patch | 42 +++-- .../neoforge/common/CommonHooks.java | 119 ++++++++++--- .../common/damagesource/DamageContainer.java | 167 ++++++++++++++++++ .../damagesource/IReductionFunction.java | 24 +++ .../extensions/ILivingEntityExtension.java | 13 ++ .../EntityInvulnerabilityCheckEvent.java | 55 ++++++ .../event/entity/living/ArmorHurtEvent.java | 74 ++++++++ .../entity/living/LivingAttackEvent.java | 50 ------ .../entity/living/LivingDamageEvent.java | 142 +++++++++++---- .../event/entity/living/LivingHurtEvent.java | 55 ------ .../living/LivingIncomingDamageEvent.java | 87 +++++++++ .../entity/living/LivingShieldBlockEvent.java | 125 +++++++++++++ .../event/entity/living/ShieldBlockEvent.java | 78 -------- .../neoforge/debug/data/DataMapTests.java | 2 +- .../debug/entity/EntityEventTests.java | 27 +++ .../entity/living/LivingEntityEventTests.java | 6 +- .../debug/entity/player/PlayerEventTests.java | 28 +++ 21 files changed, 965 insertions(+), 301 deletions(-) delete mode 100644 patches/net/minecraft/client/player/RemotePlayer.java.patch create mode 100644 src/main/java/net/neoforged/neoforge/common/damagesource/DamageContainer.java create mode 100644 src/main/java/net/neoforged/neoforge/common/damagesource/IReductionFunction.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/EntityInvulnerabilityCheckEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/ArmorHurtEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/LivingAttackEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/LivingHurtEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/LivingIncomingDamageEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/LivingShieldBlockEvent.java delete mode 100644 src/main/java/net/neoforged/neoforge/event/entity/living/ShieldBlockEvent.java diff --git a/patches/net/minecraft/client/player/LocalPlayer.java.patch b/patches/net/minecraft/client/player/LocalPlayer.java.patch index eeebdce01e..632b558ef9 100644 --- a/patches/net/minecraft/client/player/LocalPlayer.java.patch +++ b/patches/net/minecraft/client/player/LocalPlayer.java.patch @@ -1,13 +1,5 @@ --- a/net/minecraft/client/player/LocalPlayer.java +++ b/net/minecraft/client/player/LocalPlayer.java -@@ -161,6 +_,7 @@ - - @Override - public boolean hurt(DamageSource p_108662_, float p_108663_) { -+ net.neoforged.neoforge.common.CommonHooks.onPlayerAttack(this, p_108662_, p_108663_); - return false; - } - @@ -297,6 +_,7 @@ ServerboundPlayerActionPacket.Action serverboundplayeractionpacket$action = p_108701_ ? ServerboundPlayerActionPacket.Action.DROP_ALL_ITEMS diff --git a/patches/net/minecraft/client/player/RemotePlayer.java.patch b/patches/net/minecraft/client/player/RemotePlayer.java.patch deleted file mode 100644 index afa4ad0f6c..0000000000 --- a/patches/net/minecraft/client/player/RemotePlayer.java.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/net/minecraft/client/player/RemotePlayer.java -+++ b/net/minecraft/client/player/RemotePlayer.java -@@ -33,6 +_,7 @@ - - @Override - public boolean hurt(DamageSource p_108772_, float p_108773_) { -+ net.neoforged.neoforge.common.CommonHooks.onPlayerAttack(this, p_108772_, p_108773_); - return true; - } - diff --git a/patches/net/minecraft/world/entity/Entity.java.patch b/patches/net/minecraft/world/entity/Entity.java.patch index 573983871a..f8cf8c0824 100644 --- a/patches/net/minecraft/world/entity/Entity.java.patch +++ b/patches/net/minecraft/world/entity/Entity.java.patch @@ -292,6 +292,19 @@ } public boolean is(Entity p_20356_) { +@@ -2516,10 +_,11 @@ + } + + public boolean isInvulnerableTo(DamageSource p_20122_) { +- return this.isRemoved() ++ boolean isVanillaInvulnerable = this.isRemoved() + || this.invulnerable && !p_20122_.is(DamageTypeTags.BYPASSES_INVULNERABILITY) && !p_20122_.isCreativePlayer() + || p_20122_.is(DamageTypeTags.IS_FIRE) && this.fireImmune() + || p_20122_.is(DamageTypeTags.IS_FALL) && this.getType().is(EntityTypeTags.FALL_DAMAGE_IMMUNE); ++ return net.neoforged.neoforge.common.CommonHooks.isEntityInvulnerableTo(this, p_20122_, isVanillaInvulnerable); + } + + public boolean isInvulnerable() { @@ -2544,6 +_,7 @@ @Nullable diff --git a/patches/net/minecraft/world/entity/LivingEntity.java.patch b/patches/net/minecraft/world/entity/LivingEntity.java.patch index e99e001ebf..e5fd781754 100644 --- a/patches/net/minecraft/world/entity/LivingEntity.java.patch +++ b/patches/net/minecraft/world/entity/LivingEntity.java.patch @@ -9,6 +9,21 @@ private static final Logger LOGGER = LogUtils.getLogger(); private static final String TAG_ACTIVE_EFFECTS = "active_effects"; private static final ResourceLocation SPEED_MODIFIER_POWDER_SNOW_ID = ResourceLocation.withDefaultNamespace("powder_snow"); +@@ -254,6 +_,14 @@ + private boolean skipDropExperience; + private final Reference2ObjectMap> activeLocationDependentEnchantments = new Reference2ObjectArrayMap<>(); + protected float appliedScale = 1.0F; ++ /** ++ * This field stores information about damage dealt to this entity. ++ * a new {@link net.neoforged.neoforge.common.damagesource.DamageContainer} is instantiated ++ * via {@link #hurt(DamageSource, float)} after invulnerability checks, and is removed from ++ * the stack before the method's return. ++ **/ ++ @Nullable ++ protected java.util.Stack damageContainers = new java.util.Stack<>(); + + protected LivingEntity(EntityType p_20966_, Level p_20967_) { + super(p_20966_, p_20967_); @@ -320,7 +_,9 @@ .add(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE) .add(Attributes.WATER_MOVEMENT_EFFICIENCY) @@ -152,36 +167,70 @@ float f = this.getHealth(); if (f > 0.0F) { this.setHealth(f + p_21116_); -@@ -1081,6 +_,7 @@ - - @Override - public boolean hurt(DamageSource p_21016_, float p_21017_) { -+ if (!net.neoforged.neoforge.common.CommonHooks.onLivingAttack(this, p_21016_, p_21017_)) return false; - if (this.isInvulnerableTo(p_21016_)) { +@@ -1090,23 +_,30 @@ + } else if (p_21016_.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) { return false; - } else if (this.level().isClientSide) { -@@ -1099,14 +_,17 @@ + } else { ++ this.damageContainers.push(new net.neoforged.neoforge.common.damagesource.DamageContainer(p_21016_, p_21017_)); ++ if (net.neoforged.neoforge.common.CommonHooks.onEntityIncomingDamage(this, this.damageContainers.peek())) return false; + if (this.isSleeping() && !this.level().isClientSide) { + this.stopSleeping(); + } + + this.noActionTime = 0; ++ p_21017_ = this.damageContainers.peek().getNewDamage(); //Neo: enforce damage container as source of truth for damage amount + float f = p_21017_; boolean flag = false; float f1 = 0.0F; - if (p_21017_ > 0.0F && this.isDamageSourceBlocked(p_21016_)) { +- if (p_21017_ > 0.0F && this.isDamageSourceBlocked(p_21016_)) { - this.hurtCurrentlyUsedShield(p_21017_); - f1 = p_21017_; - p_21017_ = 0.0F; -+ net.neoforged.neoforge.event.entity.living.ShieldBlockEvent ev = net.neoforged.neoforge.common.CommonHooks.onShieldBlock(this, p_21016_, p_21017_); -+ if(!ev.isCanceled()) { -+ if(ev.shieldTakesDamage()) this.hurtCurrentlyUsedShield(p_21017_); ++ net.neoforged.neoforge.event.entity.living.LivingShieldBlockEvent ev; ++ if (p_21017_ > 0.0F && (ev = net.neoforged.neoforge.common.CommonHooks.onDamageBlock(this, this.damageContainers.peek(), this.isDamageSourceBlocked(p_21016_))).getBlocked()) { ++ this.damageContainers.peek().setBlockedDamage(ev); ++ if(ev.shieldDamage() > 0) { ++ this.hurtCurrentlyUsedShield(ev.shieldDamage()); ++ } + f1 = ev.getBlockedDamage(); -+ p_21017_ -= ev.getBlockedDamage(); ++ p_21017_ = ev.getDamageContainer().getNewDamage(); if (!p_21016_.is(DamageTypeTags.IS_PROJECTILE) && p_21016_.getDirectEntity() instanceof LivingEntity livingentity) { this.blockUsingShield(livingentity); } - flag = true; + flag = p_21017_ <= 0; -+ } } if (p_21016_.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) { +@@ -1118,10 +_,12 @@ + p_21017_ *= 0.75F; + } + ++ this.damageContainers.peek().setNewDamage(p_21017_); //update container with vanilla changes + this.walkAnimation.setSpeed(1.5F); + boolean flag1 = true; + if ((float)this.invulnerableTime > 10.0F && !p_21016_.is(DamageTypeTags.BYPASSES_COOLDOWN)) { + if (p_21017_ <= this.lastHurt) { ++ this.damageContainers.pop(); + return false; + } + +@@ -1130,12 +_,13 @@ + flag1 = false; + } else { + this.lastHurt = p_21017_; +- this.invulnerableTime = 20; ++ this.invulnerableTime = this.damageContainers.peek().getPostAttackInvulnerabilityTicks(); + this.actuallyHurt(p_21016_, p_21017_); + this.hurtDuration = 10; + this.hurtTime = this.hurtDuration; + } + ++ p_21017_ = this.damageContainers.peek().getNewDamage(); //update local with container value + Entity entity = p_21016_.getEntity(); + if (entity != null) { + if (entity instanceof LivingEntity livingentity1 @@ -1147,9 +_,9 @@ if (entity instanceof Player player1) { this.lastHurtByPlayerTime = 100; @@ -203,6 +252,14 @@ } } +@@ -1220,6 +_,7 @@ + CriteriaTriggers.PLAYER_HURT_ENTITY.trigger((ServerPlayer)entity, this, p_21016_, f, p_21017_, flag); + } + ++ this.damageContainers.pop(); + return flag2; + } + } @@ -1240,7 +_,7 @@ for (InteractionHand interactionhand : InteractionHand.values()) { @@ -330,9 +387,20 @@ this.playSound(soundtype.getFallSound(), soundtype.getVolume() * 0.5F, soundtype.getPitch() * 0.75F); } } -@@ -1649,9 +_,9 @@ +@@ -1616,6 +_,8 @@ + if (!(p_330394_ <= 0.0F)) { + int i = (int)Math.max(1.0F, p_330394_ / 4.0F); + ++ net.neoforged.neoforge.common.CommonHooks.onArmorHurt(p_330843_, p_331314_, p_330394_, this); ++ if (true) return; //Neo: invalidates the loop. armor damage happens in common hook + for (EquipmentSlot equipmentslot : p_331314_) { + ItemStack itemstack = this.getItemBySlot(equipmentslot); + if (itemstack.getItem() instanceof ArmorItem && itemstack.canBeHurtBy(p_330843_)) { +@@ -1648,10 +_,11 @@ + p_21194_ = Math.max(f / 25.0F, 0.0F); float f2 = f1 - p_21194_; if (f2 > 0.0F && f2 < 3.4028235E37F) { ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.MOB_EFFECTS, f2); if (this instanceof ServerPlayer) { - ((ServerPlayer)this).awardStat(Stats.DAMAGE_RESISTED, Math.round(f2 * 10.0F)); + ((ServerPlayer)this).awardStat(Stats.CUSTOM.get(Stats.DAMAGE_RESISTED), Math.round(f2 * 10.0F)); @@ -342,23 +410,44 @@ } } } -@@ -1679,6 +_,8 @@ +@@ -1670,6 +_,7 @@ + + if (f3 > 0.0F) { + p_21194_ = CombatRules.getDamageAfterMagicAbsorb(p_21194_, f3); ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ENCHANTMENTS,this.damageContainers.peek().getNewDamage() - p_21194_); + } + + return p_21194_; +@@ -1679,11 +_,14 @@ protected void actuallyHurt(DamageSource p_21240_, float p_21241_) { if (!this.isInvulnerableTo(p_21240_)) { -+ p_21241_ = net.neoforged.neoforge.common.CommonHooks.onLivingHurt(this, p_21240_, p_21241_); -+ if (p_21241_ <= 0) return; - p_21241_ = this.getDamageAfterArmorAbsorb(p_21240_, p_21241_); - p_21241_ = this.getDamageAfterMagicAbsorb(p_21240_, p_21241_); - float f1 = Math.max(p_21241_ - this.getAbsorptionAmount(), 0.0F); -@@ -1688,6 +_,7 @@ +- p_21241_ = this.getDamageAfterArmorAbsorb(p_21240_, p_21241_); +- p_21241_ = this.getDamageAfterMagicAbsorb(p_21240_, p_21241_); +- float f1 = Math.max(p_21241_ - this.getAbsorptionAmount(), 0.0F); +- this.setAbsorptionAmount(this.getAbsorptionAmount() - (p_21241_ - f1)); +- float f = p_21241_ - f1; ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ARMOR, this.damageContainers.peek().getNewDamage() - this.getDamageAfterArmorAbsorb(p_21240_, this.damageContainers.peek().getNewDamage())); ++ this.getDamageAfterMagicAbsorb(p_21240_, this.damageContainers.peek().getNewDamage()); ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION, this.getAbsorptionAmount()); ++ float f1 = Math.max(this.damageContainers.peek().getNewDamage() - this.damageContainers.peek().getReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION), 0.0F); ++ this.setAbsorptionAmount(this.damageContainers.peek().getReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION) - (this.damageContainers.peek().getNewDamage() - f1)); ++ f1 = net.neoforged.neoforge.common.CommonHooks.onLivingDamagePre(this, this.damageContainers.peek()); ++ if (f1 <= 0) return; ++ float f = this.damageContainers.peek().getNewDamage() - f1; + if (f > 0.0F && f < 3.4028235E37F && p_21240_.getEntity() instanceof ServerPlayer serverplayer) { serverplayer.awardStat(Stats.DAMAGE_DEALT_ABSORBED, Math.round(f * 10.0F)); } - -+ f1 = net.neoforged.neoforge.common.CommonHooks.onLivingDamage(this, p_21240_, f1); - if (f1 != 0.0F) { - this.getCombatTracker().recordDamage(p_21240_, f1); +@@ -1693,7 +_,9 @@ this.setHealth(this.getHealth() - f1); + this.setAbsorptionAmount(this.getAbsorptionAmount() - f1); + this.gameEvent(GameEvent.ENTITY_DAMAGE); ++ this.onDamageTaken(this.damageContainers.peek()); + } ++ net.neoforged.neoforge.common.CommonHooks.onLivingDamagePost(this, this.damageContainers.peek()); + } + } + @@ -1747,6 +_,8 @@ } diff --git a/patches/net/minecraft/world/entity/player/Player.java.patch b/patches/net/minecraft/world/entity/player/Player.java.patch index 1ee123df92..c28d275637 100644 --- a/patches/net/minecraft/world/entity/player/Player.java.patch +++ b/patches/net/minecraft/world/entity/player/Player.java.patch @@ -116,14 +116,6 @@ @Override public void readAdditionalSaveData(CompoundTag p_36215_) { super.readAdditionalSaveData(p_36215_); -@@ -859,6 +_,7 @@ - - @Override - public boolean hurt(DamageSource p_36154_, float p_36155_) { -+ if (!net.neoforged.neoforge.common.CommonHooks.onPlayerAttack(this, p_36154_, p_36155_)) return false; - if (this.isInvulnerableTo(p_36154_)) { - return false; - } else if (this.abilities.invulnerable && !p_36154_.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { @@ -872,7 +_,9 @@ this.removeEntitiesOnShoulder(); } @@ -159,20 +151,36 @@ if (this.useItem.isEmpty()) { if (interactionhand == InteractionHand.MAIN_HAND) { this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); -@@ -952,10 +_,13 @@ +@@ -952,11 +_,14 @@ @Override protected void actuallyHurt(DamageSource p_36312_, float p_36313_) { if (!this.isInvulnerableTo(p_36312_)) { -+ p_36313_ = net.neoforged.neoforge.common.CommonHooks.onLivingHurt(this, p_36312_, p_36313_); -+ if (p_36313_ <= 0) return; - p_36313_ = this.getDamageAfterArmorAbsorb(p_36312_, p_36313_); - p_36313_ = this.getDamageAfterMagicAbsorb(p_36312_, p_36313_); - float f1 = Math.max(p_36313_ - this.getAbsorptionAmount(), 0.0F); - this.setAbsorptionAmount(this.getAbsorptionAmount() - (p_36313_ - f1)); -+ f1 = net.neoforged.neoforge.common.CommonHooks.onLivingDamage(this, p_36312_, f1); - float f = p_36313_ - f1; +- p_36313_ = this.getDamageAfterArmorAbsorb(p_36312_, p_36313_); +- p_36313_ = this.getDamageAfterMagicAbsorb(p_36312_, p_36313_); +- float f1 = Math.max(p_36313_ - this.getAbsorptionAmount(), 0.0F); +- this.setAbsorptionAmount(this.getAbsorptionAmount() - (p_36313_ - f1)); +- float f = p_36313_ - f1; ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ARMOR, this.damageContainers.peek().getNewDamage() - this.getDamageAfterArmorAbsorb(p_36312_, this.damageContainers.peek().getNewDamage())); ++ this.getDamageAfterMagicAbsorb(p_36312_, this.damageContainers.peek().getNewDamage()); ++ this.damageContainers.peek().setReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION, this.getAbsorptionAmount()); ++ float f1 = Math.max(this.damageContainers.peek().getNewDamage() - this.damageContainers.peek().getReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION), 0.0F); ++ this.setAbsorptionAmount(this.damageContainers.peek().getReduction(net.neoforged.neoforge.common.damagesource.DamageContainer.Reduction.ABSORPTION) - (this.damageContainers.peek().getNewDamage() - f1)); ++ f1 = net.neoforged.neoforge.common.CommonHooks.onLivingDamagePre(this, this.damageContainers.peek()); ++ if (f1 <= 0) return; ++ float f = this.damageContainers.peek().getNewDamage() - f1; if (f > 0.0F && f < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(f * 10.0F)); + } +@@ -970,7 +_,9 @@ + } + + this.gameEvent(GameEvent.ENTITY_DAMAGE); ++ this.onDamageTaken(this.damageContainers.peek()); + } ++ net.neoforged.neoforge.common.CommonHooks.onLivingDamagePost(this, this.damageContainers.peek()); + } + } + @@ -1014,6 +_,8 @@ return InteractionResult.PASS; diff --git a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java index 874ce193eb..494cbf2652 100644 --- a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java +++ b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,6 +99,7 @@ import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.AdventureModePredicate; +import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.BucketItem; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.EnchantedBookItem; @@ -124,7 +126,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.GameMasterBlock; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.chunk.ChunkAccess; @@ -146,6 +147,7 @@ import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.common.conditions.ConditionalOps; +import net.neoforged.neoforge.common.damagesource.DamageContainer; import net.neoforged.neoforge.common.extensions.IEntityExtension; import net.neoforged.neoforge.common.loot.IGlobalLootModifier; import net.neoforged.neoforge.common.loot.LootModifierManager; @@ -165,10 +167,11 @@ import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent; import net.neoforged.neoforge.event.entity.EntityEvent; +import net.neoforged.neoforge.event.entity.EntityInvulnerabilityCheckEvent; import net.neoforged.neoforge.event.entity.EntityTravelToDimensionEvent; import net.neoforged.neoforge.event.entity.item.ItemTossEvent; +import net.neoforged.neoforge.event.entity.living.ArmorHurtEvent; import net.neoforged.neoforge.event.entity.living.EnderManAngerEvent; -import net.neoforged.neoforge.event.entity.living.LivingAttackEvent; import net.neoforged.neoforge.event.entity.living.LivingBreatheEvent; import net.neoforged.neoforge.event.entity.living.LivingChangeTargetEvent; import net.neoforged.neoforge.event.entity.living.LivingDamageEvent; @@ -178,12 +181,12 @@ import net.neoforged.neoforge.event.entity.living.LivingEvent; import net.neoforged.neoforge.event.entity.living.LivingFallEvent; import net.neoforged.neoforge.event.entity.living.LivingGetProjectileEvent; -import net.neoforged.neoforge.event.entity.living.LivingHurtEvent; +import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.living.LivingKnockBackEvent; +import net.neoforged.neoforge.event.entity.living.LivingShieldBlockEvent; import net.neoforged.neoforge.event.entity.living.LivingSwapItemsEvent; import net.neoforged.neoforge.event.entity.living.LivingUseTotemEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; -import net.neoforged.neoforge.event.entity.living.ShieldBlockEvent; import net.neoforged.neoforge.event.entity.player.AnvilRepairEvent; import net.neoforged.neoforge.event.entity.player.AttackEntityEvent; import net.neoforged.neoforge.event.entity.player.CriticalHitEvent; @@ -236,12 +239,32 @@ public static LivingChangeTargetEvent onLivingChangeTarget(LivingEntity entity, return event; } - public static boolean onLivingAttack(LivingEntity entity, DamageSource src, float amount) { - return entity instanceof Player || !NeoForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount)).isCanceled(); + /** + * Creates and posts an {@link EntityInvulnerabilityCheckEvent}. This is invoked in + * {@link Entity#isInvulnerableTo(DamageSource)} and returns a post-listener result + * to the invulnerability status of the entity to the damage source. + * + * @param entity the entity being checked for invulnerability + * @param source the damage source being applied for this check + * @param isInvul whether this entity is invulnerable according to preceding/vanilla logic + * @return if this entity is invulnerable + */ + public static boolean isEntityInvulnerableTo(Entity entity, DamageSource source, boolean isInvul) { + return NeoForge.EVENT_BUS.post(new EntityInvulnerabilityCheckEvent(entity, source, isInvul)).isInvulnerable(); } - public static boolean onPlayerAttack(LivingEntity entity, DamageSource src, float amount) { - return !NeoForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount)).isCanceled(); + /** + * Called after invulnerability checks in {@link LivingEntity#hurt(DamageSource, float)}, + * this method creates and posts the first event in the LivingEntity damage sequence, + * {@link LivingIncomingDamageEvent}. + * + * @param entity the entity to receive damage + * @param container the newly instantiated container for damage to be dealt. Most properties of + * the container will be empty at this stage. + * @return if the event is cancelled and no damage will be applied to the entity + */ + public static boolean onEntityIncomingDamage(LivingEntity entity, DamageContainer container) { + return NeoForge.EVENT_BUS.post(new LivingIncomingDamageEvent(entity, container)).isCanceled(); } public static LivingKnockBackEvent onLivingKnockBack(LivingEntity target, float strength, double ratioX, double ratioZ) { @@ -254,14 +277,57 @@ public static boolean onLivingUseTotem(LivingEntity entity, DamageSource damageS return !NeoForge.EVENT_BUS.post(new LivingUseTotemEvent(entity, damageSource, totem, hand)).isCanceled(); } - public static float onLivingHurt(LivingEntity entity, DamageSource src, float amount) { - LivingHurtEvent event = new LivingHurtEvent(entity, src, amount); - return (NeoForge.EVENT_BUS.post(event).isCanceled() ? 0 : event.getAmount()); + /** + * Creates and posts an {@link LivingDamageEvent.Pre}. This is invoked in + * {@link LivingEntity#actuallyHurt(DamageSource, float)} and {@link Player#actuallyHurt(DamageSource, float)} + * and requires access to the internal field {@link LivingEntity#damageContainers} as a parameter. + * + * @param entity the entity to receive damage + * @param container the container object holding the final values of the damage pipeline while they are still mutable + * @return the current damage value to be applied to the entity's health + * + */ + public static float onLivingDamagePre(LivingEntity entity, DamageContainer container) { + return NeoForge.EVENT_BUS.post(new LivingDamageEvent.Pre(entity, container)).getContainer().getNewDamage(); + } + + /** + * Creates and posts a {@link LivingDamageEvent.Post}. This is invoked in + * {@link LivingEntity#actuallyHurt(DamageSource, float)} and {@link Player#actuallyHurt(DamageSource, float)} + * and requires access to the internal field {@link LivingEntity#damageContainers} as a parameter. + * + * @param entity the entity to receive damage + * @param container the container object holding the truly final values of the damage pipeline. The values + * of this container and used to instantiate final fields in the event. + */ + public static void onLivingDamagePost(LivingEntity entity, DamageContainer container) { + NeoForge.EVENT_BUS.post(new LivingDamageEvent.Post(entity, container)); } - public static float onLivingDamage(LivingEntity entity, DamageSource src, float amount) { - LivingDamageEvent event = new LivingDamageEvent(entity, src, amount); - return (NeoForge.EVENT_BUS.post(event).isCanceled() ? 0 : event.getAmount()); + /** + * This is invoked in {@link LivingEntity#doHurtEquipment(DamageSource, float, EquipmentSlot...)} + * and replaces the existing item hurt and break logic with an event-sensitive version. + *
+ * Each armor slot is collected and passed into a {@link ArmorHurtEvent} and posted. If not cancelled, + * the final durability loss values for each equipment item from the event will be applied. + * + * @param source the damage source applied to the entity and armor + * @param slots an array of applicable slots for damage + * @param damage the durability damage individual items will receive + * @param armoredEntity the entity wearing the armor + */ + public static void onArmorHurt(DamageSource source, EquipmentSlot[] slots, float damage, LivingEntity armoredEntity) { + EnumMap armorMap = new EnumMap<>(EquipmentSlot.class); + for (EquipmentSlot slot : slots) { + ItemStack armorPiece = armoredEntity.getItemBySlot(slot); + if (armorPiece.isEmpty()) continue; + float damageAfterFireResist = (armorPiece.getItem() instanceof ArmorItem && armorPiece.canBeHurtBy(source)) ? damage : 0; + armorMap.put(slot, new ArmorHurtEvent.ArmorEntry(armorPiece, damageAfterFireResist)); + } + + ArmorHurtEvent event = NeoForge.EVENT_BUS.post(new ArmorHurtEvent(armorMap, armoredEntity)); + if (event.isCanceled()) return; + event.getArmorMap().forEach((slot, entry) -> entry.armorItemStack.hurtAndBreak((int) entry.newDamage, armoredEntity, slot)); } public static boolean onLivingDeath(LivingEntity entity, DamageSource src) { @@ -459,7 +525,7 @@ public static void handleBlockDrops(ServerLevel level, BlockPos pos, BlockState * Fires {@link BlockEvent.BreakEvent}, pre-emptively canceling the event based on the conditions that will cause the block to not be broken anyway. *

* Note that undoing the pre-cancel will not permit breaking the block, since the vanilla conditions will always be checked. - * + * * @param level The level * @param gameType The game type of the breaking player * @param player The breaking player @@ -789,7 +855,7 @@ public interface BiomeCallbackFunction { /** * Checks if a crop can grow by firing {@link CropGrowEvent.Pre}. - * + * * @param level The level the crop is in * @param pos The position of the crop * @param state The state of the crop @@ -808,7 +874,7 @@ public static void fireCropGrowPost(Level level, BlockPos pos, BlockState state) /** * Fires the {@link CriticalHitEvent} and returns the resulting event. - * + * * @param player The attacking player * @param target The attack target * @param vanillaCritical If the attack would have been a critical hit by vanilla's rules in {@link Player#attack(Entity)}. @@ -999,8 +1065,19 @@ public static void onEntityEnterSection(Entity entity, long packedOldPos, long p NeoForge.EVENT_BUS.post(new EntityEvent.EnteringSection(entity, packedOldPos, packedNewPos)); } - public static ShieldBlockEvent onShieldBlock(LivingEntity blocker, DamageSource source, float blocked) { - ShieldBlockEvent e = new ShieldBlockEvent(blocker, source, blocked); + /** + * Creates, posts, and returns a {@link LivingShieldBlockEvent}. This method is invoked in + * {@link LivingEntity#hurt(DamageSource, float)} and requires internal access to the top entry + * in the protected field {@link LivingEntity#damageContainers} as a parameter. + * + * @param blocker the entity performing the block + * @param container the entity's internal damage container for accessing current values + * in the damage pipeline at the time of this invocation. + * @param originalBlocked whether this entity is blocking according to preceding/vanilla logic + * @return the event object after event listeners have been invoked. + */ + public static LivingShieldBlockEvent onDamageBlock(LivingEntity blocker, DamageContainer container, boolean originalBlocked) { + LivingShieldBlockEvent e = new LivingShieldBlockEvent(blocker, container, originalBlocked); NeoForge.EVENT_BUS.post(e); return e; } @@ -1375,7 +1452,7 @@ public static void onChunkUnload(PoiManager poiManager, ChunkAccess chunkAccess) /** * Checks if a mob effect can be applied to an entity by firing {@link MobEffectEvent.Applicable}. - * + * * @param entity The target entity the mob effect is being applied to. * @param effect The mob effect being applied. * @return True if the mob effect can be applied, otherwise false. @@ -1389,7 +1466,7 @@ public static boolean canMobEffectBeApplied(LivingEntity entity, MobEffectInstan * Attempts to resolve a {@link RegistryLookup} using the current global state. *

* Prioritizes the server's lookup, only attempting to retrieve it from the client if the server is unavailable. - * + * * @param The type of registry being looked up * @param key The resource key for the target registry * @return A registry access, if one was available. diff --git a/src/main/java/net/neoforged/neoforge/common/damagesource/DamageContainer.java b/src/main/java/net/neoforged/neoforge/common/damagesource/DamageContainer.java new file mode 100644 index 0000000000..5a912d1d87 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/damagesource/DamageContainer.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.damagesource; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +import net.neoforged.neoforge.event.entity.EntityInvulnerabilityCheckEvent; +import net.neoforged.neoforge.event.entity.living.LivingDamageEvent; +import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; +import net.neoforged.neoforge.event.entity.living.LivingShieldBlockEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * DamageContainer encapsulates aspects of the entity damage sequence so that + * relevant context related to damage dealt is accessible throughout the entire + * sequence. + *

Note: certain values will be defaults until the stage in the sequence when they are set.

+ *

The Damage Sequence

+ *
    + *
  1. {@link LivingEntity#hurt} is invoked on the recipient from the source of + * the attack.
  2. + *
  3. {@link Entity#isInvulnerableTo} is invoked and fires {@link EntityInvulnerabilityCheckEvent}.
  4. + *
  5. After determining the entity is vulnerable, the {@link DamageContainer} in instantiated for the entity.
  6. + *
  7. {@link LivingIncomingDamageEvent} is fired.
  8. + *
  9. {@link LivingShieldBlockEvent} fires and the result determines if shield effects apply.
  10. + *
  11. {@link LivingEntity#actuallyHurt} is called.
  12. + *
  13. armor, magic, mob_effect, and absorption reductions are captured in the DamageContainer.
  14. + *
  15. {@link LivingDamageEvent.Pre} is fired.
  16. + *
  17. if the damage is not zero, entity health is modified and recorded and {@link LivingDamageEvent.Post} is fired.
  18. + *
+ */ +@ApiStatus.Internal +public class DamageContainer { + public enum Reduction { + /** Damage reduced from the effects of armor. */ + ARMOR, + /** Damage reduced from enchantments on armor. */ + ENCHANTMENTS, + /** Damage reduced from active mob effects. */ + MOB_EFFECTS, + /** Damage absorbed by absorption. */ + ABSORPTION + } + + private final EnumMap> reductionFunctions = new EnumMap<>(Reduction.class); + private final float originalDamage; + private final DamageSource source; + private float newDamage; + private final EnumMap reductions = new EnumMap<>(Reduction.class); + private float blockedDamage = 0f; + private float shieldDamage = 0; + private int invulnerabilityTicksAfterAttack = 20; + + public DamageContainer(DamageSource source, float originalDamage) { + this.source = source; + this.originalDamage = originalDamage; + this.newDamage = originalDamage; + } + + /** {@return the value passed into {@link LivingEntity#hurt(DamageSource, float)} before any modifications are made} */ + public float getOriginalDamage() { + return originalDamage; + } + + /** {@return the damage source for this damage sequence} */ + public DamageSource getSource() { + return source; + } + + /** + * This sets the current damage value for the entity at the stage of the damage sequence in which it is set. + * Subsequent steps in the damage sequence will use and modify this value accordingly. If this is called in + * the final step of the sequence, this value will be applied against the entity's health. + * + * @param damage the amount to harm this entity at the end of the damage sequence + */ + public void setNewDamage(float damage) { + this.newDamage = damage; + } + + /** {@return the current amount expected to be applied to the entity or used in subsequent damage calculations} */ + public float getNewDamage() { + return newDamage; + } + + /** + * Adds a callback modifier to the vanilla damage reductions. Each function will be performed in sequence + * on the vanilla value at the time the {@link DamageContainer.Reduction} type is set by vanilla. + *

Note: only the {@link LivingIncomingDamageEvent EntityPreDamageEvent} + * happens early enough in the sequence for this method to have any effect.

+ * + * @param type The reduction type your function will apply to + * @param reductionFunction takes the current reduction from vanilla and any preceding functions and returns a new + * value for the reduction. These are always executed in insertion order. if sequence + * matters, use {@link net.neoforged.bus.api.EventPriority} to order your function. + */ + + public void addModifier(Reduction type, IReductionFunction reductionFunction) { + this.reductionFunctions.computeIfAbsent(type, a -> new ArrayList<>()).add(reductionFunction); + } + + /** {@return the damage blocked during the {@link LivingShieldBlockEvent}} */ + public float getBlockedDamage() { + return blockedDamage; + } + + /** {@return the durability applied to the applicable shield after {@link LivingShieldBlockEvent} returned a successful block} */ + public float getShieldDamage() { + return shieldDamage; + } + + /** + * Explicitly sets the invulnerability ticks after the damage has been applied. + * + * @param ticks Ticks of invulnerability after this damage sequence + */ + public void setPostAttackInvulnerabilityTicks(int ticks) { + this.invulnerabilityTicksAfterAttack = ticks; + } + + /** {@return the number of ticks this entity will be invulnerable after damage is applied} */ + public int getPostAttackInvulnerabilityTicks() { + return invulnerabilityTicksAfterAttack; + } + + /** + * This provides a post-reduction value for the reduction and modifiers. This will always return zero + * before {@link LivingDamageEvent.Pre} and will consume all + * modifiers prior to the event. + * + * @param type the specific source type of the damage reduction + * @return The amount of damage reduced by armor after vanilla armor reductions and added modifiers + */ + public float getReduction(Reduction type) { + return reductions.getOrDefault(type, 0f); + } + + //=============INTERNAL METHODS - DO NOT USE=================== + + @ApiStatus.Internal + public void setBlockedDamage(LivingShieldBlockEvent event) { + if (event.getBlocked()) { + this.blockedDamage = event.getBlockedDamage(); + this.shieldDamage = event.shieldDamage(); + this.newDamage -= this.blockedDamage; + } + } + + @ApiStatus.Internal + public void setReduction(Reduction reduction, float amount) { + this.reductions.put(reduction, modifyReduction(Reduction.ABSORPTION, amount)); + this.newDamage -= Math.max(0, amount); + } + + private float modifyReduction(Reduction type, float reduction) { + for (var func : reductionFunctions.getOrDefault(type, new ArrayList<>())) { + reduction = func.modify(this, reduction); + } + return reduction; + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/damagesource/IReductionFunction.java b/src/main/java/net/neoforged/neoforge/common/damagesource/IReductionFunction.java new file mode 100644 index 0000000000..03e16f18a9 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/damagesource/IReductionFunction.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.damagesource; + +/** + * An {@link IReductionFunction} is used by {@link DamageContainer} instances.
+ * This allows sequential modification of damage reduction values to be stored and + * later invoked before actual reductions are applied to the damage sequence. + */ +@FunctionalInterface +public interface IReductionFunction { + /** + * Consumes an existing reduction value and produces a modified value. + * + * @param container the {@link DamageContainer} representing the damage sequence + * values for the reduction being modified + * @param reductionIn the initial or preceding reduction value to this operation + * @return the new reduction value + */ + float modify(DamageContainer container, float reductionIn); +} diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/ILivingEntityExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/ILivingEntityExtension.java index 3c071ad62c..2d03a767b5 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/ILivingEntityExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/ILivingEntityExtension.java @@ -5,10 +5,12 @@ package net.neoforged.neoforge.common.extensions; +import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.common.damagesource.DamageContainer; import net.neoforged.neoforge.fluids.FluidType; public interface ILivingEntityExtension extends IEntityExtension { @@ -64,4 +66,15 @@ default boolean canDrownInFluidType(FluidType type) { default boolean moveInFluid(FluidState state, Vec3 movementVector, double gravity) { return state.move(self(), movementVector, gravity); } + + /** + * Executes in {@link LivingEntity#hurt(DamageSource, float)} after all damage and + * effects have applied. Overriding this method is preferred over overriding the + * hurt method in custom entities where special behavior is desired after vanilla + * logic. + * + * @param damageContainer The aggregated damage details preceding this hook, which + * includes changes made to the damage sequence by events. + */ + default void onDamageTaken(DamageContainer damageContainer) {} } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/EntityInvulnerabilityCheckEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/EntityInvulnerabilityCheckEvent.java new file mode 100644 index 0000000000..51889eb3a0 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/EntityInvulnerabilityCheckEvent.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity; + +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired when {@link Entity#isInvulnerableTo(DamageSource)} is invoked and determines if + * downstream hurt logic should apply. This event is fired on both sides in + * {@link Entity#isInvulnerableTo(DamageSource)} + *
+ * Note: This event may be unable to change the invulnerable status of some entities + * that override isInvulnerableTo against certain damage sources + */ +public class EntityInvulnerabilityCheckEvent extends EntityEvent { + private final boolean originallyInvulnerable; + private boolean isInvulnerable; + private final DamageSource source; + + @ApiStatus.Internal + public EntityInvulnerabilityCheckEvent(Entity entity, DamageSource source, boolean isVanillaInvulnerable) { + super(entity); + this.originallyInvulnerable = isVanillaInvulnerable; + this.isInvulnerable = isVanillaInvulnerable; + this.source = source; + } + + /** + * Sets the invulnerable status of the entity. By default, the invulnerability will be + * set by value passed into the event invocation. + */ + public void setInvulnerable(boolean isInvulnerable) { + this.isInvulnerable = isInvulnerable; + } + + /** @return the current invulnerability state */ + public boolean isInvulnerable() { + return isInvulnerable; + } + + /** @return an immutable reference to the damage source being applied to this entity */ + public DamageSource getSource() { + return source; + } + + /** @return the invulnerability status passed into the event by vanilla */ + public boolean getOriginalInvulnerability() { + return originallyInvulnerable; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/ArmorHurtEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/ArmorHurtEvent.java new file mode 100644 index 0000000000..56057ef1e4 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/ArmorHurtEvent.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.living; + +import java.util.EnumMap; +import java.util.Map; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.neoforged.bus.api.ICancellableEvent; +import org.jetbrains.annotations.ApiStatus; + +/** + * Fired on both sides when a {@link LivingEntity}'s armor is dealt damage in + * {@link LivingEntity#doHurtEquipment(DamageSource, float, EquipmentSlot...) doHurtEquipment}. + */ +public class ArmorHurtEvent extends LivingEvent implements ICancellableEvent { + public static class ArmorEntry { + public ItemStack armorItemStack; + public final float originalDamage; + public float newDamage; + + public ArmorEntry(ItemStack armorStack, float damageIn) { + this.armorItemStack = armorStack; + this.originalDamage = damageIn; + this.newDamage = damageIn; + } + } + + private final EnumMap armorEntries; + + @ApiStatus.Internal + public ArmorHurtEvent(EnumMap armorMap, LivingEntity player) { + super(player); + this.armorEntries = armorMap; + } + + /** + * Provides the Itemstack for the given slot. Hand slots will always return {@link ItemStack#EMPTY} + * + * @return the {@link ItemStack} to be hurt for the given slot + */ + public ItemStack getArmorItemStack(EquipmentSlot slot) { + return armorEntries.containsKey(slot) ? armorEntries.get(slot).armorItemStack : ItemStack.EMPTY; + } + + /** {@return the original damage before any event modifications} */ + public Float getOriginalDamage(EquipmentSlot slot) { + return armorEntries.containsKey(slot) ? armorEntries.get(slot).originalDamage : 0f; + } + + /** {@return the amount to hurt the armor if the event is not cancelled} */ + public Float getNewDamage(EquipmentSlot slot) { + return armorEntries.containsKey(slot) ? armorEntries.get(slot).newDamage : 0f; + } + + /** + * Sets new damage for the armor. Setting damage for empty slots will have no effect. + * + * @param damage the new amount to hurt the armor. Values below zero will be set to zero. + */ + public void setNewDamage(EquipmentSlot slot, float damage) { + if (this.armorEntries.containsKey(slot)) this.armorEntries.get(slot).newDamage = damage; + } + + /** Used internally to get the full map of {@link ItemStack}s to be hurt */ + public Map getArmorMap() { + return armorEntries; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingAttackEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingAttackEvent.java deleted file mode 100644 index 7ab20fe52b..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingAttackEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.living; - -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.neoforged.bus.api.ICancellableEvent; -import net.neoforged.neoforge.common.CommonHooks; -import net.neoforged.neoforge.common.NeoForge; - -/** - * LivingAttackEvent is fired when a living Entity is attacked.
- * This event is fired whenever an Entity is attacked in - * {@link LivingEntity#hurt(DamageSource, float)} and - * {@link Player#hurt(DamageSource, float)}.
- *
- * This event is fired via the {@link CommonHooks#onLivingAttack(LivingEntity, DamageSource, float)}.
- *
- * {@link #source} contains the DamageSource of the attack.
- * {@link #amount} contains the amount of damage dealt to the entity.
- *
- * This event is {@link net.neoforged.bus.api.ICancellableEvent}.
- * If this event is canceled, the Entity does not take attack damage.
- *
- * This event does not have a result. {@link HasResult}
- *
- * This event is fired on the {@link NeoForge#EVENT_BUS}. - **/ -public class LivingAttackEvent extends LivingEvent implements ICancellableEvent { - private final DamageSource source; - private final float amount; - - public LivingAttackEvent(LivingEntity entity, DamageSource source, float amount) { - super(entity); - this.source = source; - this.amount = amount; - } - - public DamageSource getSource() { - return source; - } - - public float getAmount() { - return amount; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingDamageEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingDamageEvent.java index 91b3a98486..b406448858 100644 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingDamageEvent.java +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingDamageEvent.java @@ -1,54 +1,132 @@ /* - * Copyright (c) Forge Development LLC and contributors + * Copyright (c) NeoForged and contributors * SPDX-License-Identifier: LGPL-2.1-only */ package net.neoforged.neoforge.event.entity.living; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.stream.Collectors; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; -import net.neoforged.bus.api.ICancellableEvent; import net.neoforged.neoforge.common.CommonHooks; +import net.neoforged.neoforge.common.damagesource.DamageContainer; /** - * LivingDamageEvent is fired just before damage is applied to entity.
- * At this point armor, potion and absorption modifiers have already been applied to damage - this is FINAL value.
- * Also note that appropriate resources (like armor durability and absorption extra hearths) have already been consumed.
- * This event is fired whenever an Entity is damaged in - * {@code LivingEntity#actuallyHurt(DamageSource, float)} and - * {@code Player#actuallyHurt(DamageSource, float)}.
+ * LivingDamageEvent captures an entity's loss of health. At this stage in + * the damage sequence, all reduction effects have been applied. *
- * This event is fired via the {@link CommonHooks#onLivingDamage(LivingEntity, DamageSource, float)}.
+ * {@link Pre} allows for modification of the damage value before it is applied + * to the entity's health. *
- * {@link #source} contains the DamageSource that caused this Entity to be hurt.
- * {@link #amount} contains the final amount of damage that will be dealt to entity.
- *
- * This event is {@link ICancellableEvent}.
- * If this event is canceled, the Entity is not hurt. Used resources WILL NOT be restored.
- *
- * This event does not have a result. {@link HasResult}
+ * {@link Post} contains an immutable representation of the entire damage sequence + * and allows for reference to the values accrued at each step. * - * @see LivingHurtEvent - **/ -public class LivingDamageEvent extends LivingEvent implements ICancellableEvent { - private final DamageSource source; - private float amount; - - public LivingDamageEvent(LivingEntity entity, DamageSource source, float amount) { + * @see DamageContainer for more information on the damage sequence + */ +public abstract class LivingDamageEvent extends LivingEvent { + private LivingDamageEvent(LivingEntity entity) { super(entity); - this.source = source; - this.amount = amount; } - public DamageSource getSource() { - return source; - } + /** + * LivingDamageEvent.Pre is fired when an Entity is set to be hurt.
+ * At this point armor, potion and absorption modifiers have already been applied to the damage value + * and the entity.
+ * This event is fired in {@code LivingEntity#actuallyHurt(DamageSource, float} + *
+ * For custom posting of this event, the event expects to be fired after + * damage reductions have been calculated but before any changes to the entity + * health has been applied. This event expects a mutable {@link DamageContainer}. + *
+ * This event is fired via the {@link CommonHooks#onLivingDamagePre(LivingEntity, DamageContainer)}. + * + * @see DamageContainer for more information on the damage sequence + **/ + public static class Pre extends LivingDamageEvent { + private final DamageContainer container; + + public Pre(LivingEntity entity, DamageContainer container) { + super(entity); + this.container = container; + } - public float getAmount() { - return amount; + public DamageContainer getContainer() { + return container; + } } - public void setAmount(float amount) { - this.amount = amount; + /** + * LivingDamageEvent.Post is fired after health is modified on the entity.
+ * The fields in this event represent the FINAL values of what was applied to the entity. + *
+ * Also note that appropriate resources (like armor durability and absorption extra hearts) have already been consumed.
+ * This event is fired whenever an Entity is damaged in {@code LivingEntity#actuallyHurt(DamageSource, float)} + *
+ * This event is fired via {@link CommonHooks#onLivingDamagePost(LivingEntity, DamageContainer)}. + * + * @see DamageContainer for more information on the damage sequence + **/ + public static class Post extends LivingDamageEvent { + private final float originalDamage; + private final DamageSource source; + private final float newDamage; + private final float blockedDamage; + private final float shieldDamage; + private final int postAttackInvulnerabilityTicks; + private final EnumMap reductions; + + public Post(LivingEntity entity, DamageContainer container) { + super(entity); + this.originalDamage = container.getOriginalDamage(); + this.source = container.getSource(); + this.newDamage = container.getNewDamage(); + this.blockedDamage = container.getBlockedDamage(); + this.shieldDamage = container.getShieldDamage(); + this.postAttackInvulnerabilityTicks = container.getPostAttackInvulnerabilityTicks(); + this.reductions = new EnumMap(Arrays.stream(DamageContainer.Reduction.values()) + .map(type -> new AbstractMap.SimpleEntry<>(type, container.getReduction(type))) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue))); + } + + /** {@return the original damage when {@link LivingEntity#hurt} was invoked} */ + public float getOriginalDamage() { + return originalDamage; + } + + /** {@return the {@link DamageSource} for this damage sequence} */ + public DamageSource getSource() { + return source; + } + + /** {@return the amount of health this entity lost during this sequence} */ + public float getNewDamage() { + return newDamage; + } + + /** {@return the amount of damage reduced by a blocking action} */ + public float getBlockedDamage() { + return blockedDamage; + } + + /** {@return the amount of shield durability this entity lost if a blocking action was captured and the entity was holding a shield} */ + public float getShieldDamage() { + return shieldDamage; + } + + /** {@return the number of ticks this entity will be invulnerable after this sequence} */ + public int getPostAttackInvulnerabilityTicks() { + return postAttackInvulnerabilityTicks; + } + + /** + * @param reduction the type of reduction to obtain + * @return the amount of damage reduced by this reduction type. + */ + public float getReduction(DamageContainer.Reduction reduction) { + return reductions.getOrDefault(reduction, 0f); + } } } diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingHurtEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingHurtEvent.java deleted file mode 100644 index b337a260e5..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingHurtEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.living; - -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.LivingEntity; -import net.neoforged.bus.api.ICancellableEvent; -import net.neoforged.neoforge.common.CommonHooks; -import net.neoforged.neoforge.common.NeoForge; - -/** - * LivingHurtEvent is fired when an Entity is set to be hurt.
- * This event is fired whenever an Entity is hurt in - * {@code LivingEntity#actuallyHurt(DamageSource, float)} and - * {@code Player#actuallyHurt(DamageSource, float)}.
- *
- * This event is fired via the {@link CommonHooks#onLivingHurt(LivingEntity, DamageSource, float)}.
- *
- * {@link #source} contains the DamageSource that caused this Entity to be hurt.
- * {@link #amount} contains the amount of damage dealt to the Entity that was hurt.
- *
- * This event is {@link ICancellableEvent}.
- * If this event is canceled, the Entity is not hurt.
- *
- * This event does not have a result. {@link HasResult}
- *
- * This event is fired on the {@link NeoForge#EVENT_BUS}. - * - * @see LivingDamageEvent - **/ -public class LivingHurtEvent extends LivingEvent implements ICancellableEvent { - private final DamageSource source; - private float amount; - - public LivingHurtEvent(LivingEntity entity, DamageSource source, float amount) { - super(entity); - this.source = source; - this.amount = amount; - } - - public DamageSource getSource() { - return source; - } - - public float getAmount() { - return amount; - } - - public void setAmount(float amount) { - this.amount = amount; - } -} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingIncomingDamageEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingIncomingDamageEvent.java new file mode 100644 index 0000000000..ae30de4bd4 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingIncomingDamageEvent.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.living; + +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.neoforge.common.CommonHooks; +import net.neoforged.neoforge.common.damagesource.DamageContainer; +import net.neoforged.neoforge.common.damagesource.IReductionFunction; + +/** + * LivingIncomingDamageEvent is fired when a LivingEntity is about to receive damage. + *
+ * This event is fired in {@link LivingEntity#hurt(DamageSource, float)} + * after invulnerability checks but before any damage processing/mitigation. + *
+ * For custom posting of this event, the event expects to be fired before any + * damage reductions have been calculated. This event expects a mutable {@link DamageContainer}. + *
+ * This event is fired via the {@link CommonHooks#onEntityIncomingDamage(LivingEntity, DamageContainer)}. + * + * @see DamageContainer for more information on the damage sequence + **/ +public class LivingIncomingDamageEvent extends LivingEvent implements ICancellableEvent { + private final DamageContainer container; + + public LivingIncomingDamageEvent(LivingEntity entity, DamageContainer container) { + super(entity); + this.container = container; + } + + /** {@return the container for this damage sequence} */ + public DamageContainer getContainer() { + return this.container; + } + + /** {@return the {@link DamageSource} for this damage sequence} */ + public DamageSource getSource() { + return this.container.getSource(); + } + + /** {@return the current damage to be applied to the entity} */ + public float getAmount() { + return this.container.getNewDamage(); + } + + /** {@return the damage value passed into the damage sequence before modifications} */ + public float getOriginalAmount() { + return this.container.getOriginalDamage(); + } + + /** + * @param newDamage the damage value to be used in the rest of the damage sequence. + */ + public void setAmount(float newDamage) { + this.container.setNewDamage(newDamage); + } + + /** + * Reduction modifiers alter the vanilla damage reduction before it modifies the damage value. + * Modifiers are executed in sequence. + * + * @param type the reduction type to be modified + * @param reductionFunc the function to apply to the reduction value. + */ + public void addReductionModifier(DamageContainer.Reduction type, IReductionFunction reductionFunc) { + this.container.addModifier(type, reductionFunc); + } + + /** + * When an entity's invulnerable time is fully cooled down, 20 ticks of invulnerability is added + * on the next attack. This method allows for setting a new invulnerability tick count when those + * conditions are met. + *
+ * Note: this value will be ignored if the damage is taken while invulnerability ticks are greater + * than 10 and the damage source does not bypass invulnerability + * + * @param ticks the number of ticks for the entity to remain invulnerable to incoming damage + */ + public void setInvulnerabilityTicks(int ticks) { + this.container.setPostAttackInvulnerabilityTicks(ticks); + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/LivingShieldBlockEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingShieldBlockEvent.java new file mode 100644 index 0000000000..6207535891 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/event/entity/living/LivingShieldBlockEvent.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.event.entity.living; + +import net.minecraft.util.Mth; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.LivingEntity; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.neoforge.common.damagesource.DamageContainer; + +/** + * LivingShieldBlockEvent is fired when an entity is hurt and vanilla checks if the entity is attempting + * to block with a shield.
+ * Cancelling this event will have the same impact as if the shield was not eligible to block.
+ * The damage blocked cannot be set lower than zero or greater than the original value.
+ *

Note: This event fires whether the player is actively using a shield or not. Vanilla shield + * blocking logic is captured and passed into the event via {@link #getOriginalBlock()}. If this is + * true, The shield item stack "should" be available from {@link LivingEntity#getUseItem()} at least + * for players.

+ * + * @see DamageContainer for more information on the damage sequence + */ +public class LivingShieldBlockEvent extends LivingEvent implements ICancellableEvent { + private final DamageContainer container; + private float dmgBlocked; + private float shieldDamage = -1; + private final boolean originalBlocked; + private boolean newBlocked; + + public LivingShieldBlockEvent(LivingEntity blocker, DamageContainer container, boolean originalBlockedState) { + super(blocker); + this.container = container; + this.dmgBlocked = container.getNewDamage(); + this.originalBlocked = originalBlockedState; + this.newBlocked = originalBlockedState; + this.shieldDamage = container.getNewDamage(); + } + + public DamageContainer getDamageContainer() { + return this.container; + } + + /** + * @return The damage source. + */ + public DamageSource getDamageSource() { + return this.getDamageContainer().getSource(); + } + + /** + * @return The original amount of damage blocked, which is the same as the original + * incoming damage value. + */ + public float getOriginalBlockedDamage() { + return this.getDamageContainer().getNewDamage(); + } + + /** + * @return The current amount of damage blocked, as a result of this event. + */ + public float getBlockedDamage() { + return Math.min(this.dmgBlocked, container.getNewDamage()); + } + + /** + * If the event is {@link #getBlocked()} and the user is holding a shield, the returned amount + * will be taken from the item's durability. + * + * @return The amount of shield durability damage to take. + */ + public float shieldDamage() { + if (newBlocked) + return shieldDamage >= 0 ? shieldDamage : getBlockedDamage(); + return 0; + } + + /** + * Set how much damage is blocked by this action.
+ * Note that initially the blocked amount is the entire attack.
+ */ + public void setBlockedDamage(float blocked) { + this.dmgBlocked = Mth.clamp(blocked, 0, this.getOriginalBlockedDamage()); + } + + /** + * Set how much durability the shield will lose if {@link #getBlocked()} is true. + * + * @param damage the new durability value taken from the shield on successful block + */ + public void setShieldDamage(float damage) { + this.shieldDamage = damage; + } + + /** + * @return whether the damage would have been blocked by vanilla logic + */ + public boolean getOriginalBlock() { + return originalBlocked; + } + + /** + * Used in {@link LivingEntity#hurt(DamageSource, float)} to signify that a blocking + * action has occurred. If returning false, damage to the shield will not occur. + * + * @return true if the entity should be considered "blocking" + */ + public boolean getBlocked() { + return newBlocked; + } + + /** + * Sets the blocking state of the entity. By default, entities raising a shield, + * facing the damage source, and not being hit by a source that bypasses shields + * will be considered blocking. An entity can be considered blocking regardless + * by supplying true to this. + * + * @param isBlocked should the entity be treated as if it is blocking + */ + public void setBlocked(boolean isBlocked) { + this.newBlocked = isBlocked; + } +} diff --git a/src/main/java/net/neoforged/neoforge/event/entity/living/ShieldBlockEvent.java b/src/main/java/net/neoforged/neoforge/event/entity/living/ShieldBlockEvent.java deleted file mode 100644 index 3f5f9275e6..0000000000 --- a/src/main/java/net/neoforged/neoforge/event/entity/living/ShieldBlockEvent.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.event.entity.living; - -import net.minecraft.util.Mth; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.LivingEntity; -import net.neoforged.bus.api.ICancellableEvent; - -/** - * The ShieldBlockEvent is fired when an entity successfully blocks with a shield.
- * Cancelling this event will have the same impact as if the shield was not eligible to block.
- * The damage blocked cannot be set lower than zero or greater than the original value.
- * Note: The shield item stack "should" be available from {@link LivingEntity#getUseItem()} - * at least for players. - */ -public class ShieldBlockEvent extends LivingEvent implements ICancellableEvent { - private final DamageSource source; - private final float originalBlocked; - private float dmgBlocked; - private boolean shieldTakesDamage = true; - - public ShieldBlockEvent(LivingEntity blocker, DamageSource source, float blocked) { - super(blocker); - this.source = source; - this.originalBlocked = blocked; - this.dmgBlocked = blocked; - } - - /** - * @return The damage source. - */ - public DamageSource getDamageSource() { - return this.source; - } - - /** - * @return The original amount of damage blocked, which is the same as the original - * incoming damage value. - */ - public float getOriginalBlockedDamage() { - return this.originalBlocked; - } - - /** - * @return The current amount of damage blocked, as a result of this event. - */ - public float getBlockedDamage() { - return this.dmgBlocked; - } - - /** - * Controls if {@link LivingEntity#hurtCurrentlyUsedShield} is called. - * - * @return If the shield item will take durability damage or not. - */ - public boolean shieldTakesDamage() { - return this.shieldTakesDamage; - } - - /** - * Set how much damage is blocked by this action.
- * Note that initially the blocked amount is the entire attack.
- */ - public void setBlockedDamage(float blocked) { - this.dmgBlocked = Mth.clamp(blocked, 0, this.originalBlocked); - } - - /** - * Set if the shield will take durability damage or not. - */ - public void setShieldTakesDamage(boolean damage) { - this.shieldTakesDamage = damage; - } -} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java index 565c32db90..cdcc11f3fa 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/DataMapTests.java @@ -276,7 +276,7 @@ protected void gather() { } }); - test.eventListeners().forge().addListener((final LivingDamageEvent event) -> { + test.eventListeners().forge().addListener((final LivingDamageEvent.Post event) -> { final ExperienceGrant grant = event.getSource().typeHolder().getData(xpGrant); if (grant != null && event.getEntity() instanceof Player player) { player.giveExperiencePoints(grant.amount()); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java index b775d16416..02b100e274 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java @@ -5,25 +5,32 @@ package net.neoforged.neoforge.debug.entity; +import java.util.Objects; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.gametest.framework.GameTest; +import net.minecraft.network.chat.Component; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.ai.attributes.RangedAttribute; import net.minecraft.world.entity.animal.Cow; import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent; +import net.neoforged.neoforge.event.entity.EntityInvulnerabilityCheckEvent; import net.neoforged.neoforge.event.entity.EntityTeleportEvent; import net.neoforged.neoforge.event.level.ExplosionKnockbackEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.gametest.EmptyTemplate; +import net.neoforged.testframework.gametest.GameTestPlayer; import net.neoforged.testframework.registration.RegistrationHelper; @ForEachTest(groups = { EntityTests.GROUP + ".event", "event" }) @@ -75,6 +82,26 @@ static void entityAttributeModificationEvent(final DynamicTest test, final Regis .thenSucceed()); } + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if EntityInvulnerabilityCheckEvent prevents damage when modified.") + static void entityInvulnerabilityCheckEvent(final DynamicTest test, final RegistrationHelper reg) { + final Component NAME = Component.literal("invulnerable_entity"); + test.eventListeners().forge().addListener((final EntityInvulnerabilityCheckEvent event) -> { + if (event.getEntity() instanceof GameTestPlayer entity && entity.hasCustomName() && Objects.equals(entity.getCustomName(), NAME)) + event.setInvulnerable(false); + }); + + test.onGameTest(helper -> { + DamageSource source = new DamageSource(helper.getLevel().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(DamageTypes.MOB_ATTACK)); + helper.startSequence(() -> helper.makeTickingMockServerPlayerInLevel(GameType.SURVIVAL)) + .thenExecute(player -> player.setCustomName(NAME)) + .thenExecute(player -> player.setInvulnerable(true)) + .thenWaitUntil(player -> helper.assertFalse(player.isInvulnerableTo(source), "Player Invulnerability not bypassed.")) + .thenSucceed(); + }); + } + @GameTest @EmptyTemplate(value = "15x5x15", floor = true) @TestHolder(description = "Tests if the pig only gets vertical knockback from explosion knockback event") diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/living/LivingEntityEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/living/LivingEntityEventTests.java index 27c6f9158b..647ad33bd2 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/living/LivingEntityEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/living/LivingEntityEventTests.java @@ -43,9 +43,9 @@ import net.neoforged.neoforge.event.entity.living.LivingConversionEvent; import net.neoforged.neoforge.event.entity.living.LivingEntityUseItemEvent; import net.neoforged.neoforge.event.entity.living.LivingGetProjectileEvent; +import net.neoforged.neoforge.event.entity.living.LivingShieldBlockEvent; import net.neoforged.neoforge.event.entity.living.LivingSwapItemsEvent; import net.neoforged.neoforge.event.entity.living.MobSplitEvent; -import net.neoforged.neoforge.event.entity.living.ShieldBlockEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -188,8 +188,8 @@ static void setAttackTargetEvent(final DynamicTest test, final RegistrationHelpe @EmptyTemplate(floor = true) @TestHolder(description = "Tests if the ShieldBlockEvent is fired") static void shieldBlockEvent(final DynamicTest test) { - test.eventListeners().forge().addListener((final ShieldBlockEvent event) -> { - if (event.getDamageSource().getDirectEntity() instanceof AbstractArrow arrow && event.getEntity() instanceof Zombie zombie && Objects.equals(zombie.getName(), Component.literal("shieldblock"))) { + test.eventListeners().forge().addListener((final LivingShieldBlockEvent event) -> { + if (event.getBlocked() && event.getDamageSource().getDirectEntity() instanceof AbstractArrow arrow && event.getEntity() instanceof Zombie zombie && Objects.equals(zombie.getName(), Component.literal("shieldblock"))) { zombie.setItemSlot(EquipmentSlot.OFFHAND, new ItemStack(Items.STONE)); event.setBlockedDamage(event.getOriginalBlockedDamage() / 2); arrow.discard(); diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java index e2a055d4c9..a0b17ec16c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/player/PlayerEventTests.java @@ -9,6 +9,7 @@ import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.registries.Registries; import net.minecraft.gametest.framework.GameTest; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.ServerboundInteractPacket; @@ -17,9 +18,13 @@ import net.minecraft.stats.Stats; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageTypes; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -31,6 +36,7 @@ import net.minecraft.world.level.portal.DimensionTransition; import net.neoforged.bus.api.EventPriority; import net.neoforged.neoforge.event.StatAwardEvent; +import net.neoforged.neoforge.event.entity.living.ArmorHurtEvent; import net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent; import net.neoforged.neoforge.event.entity.player.PermissionsChangedEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; @@ -232,6 +238,28 @@ static void changeStatAward(final DynamicTest test, final RegistrationHelper reg }); } + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if ArmorHurtEvent fires and prevents armor damage.") + static void armorHurtEvent(final DynamicTest test) { + test.eventListeners().forge().addListener((final ArmorHurtEvent event) -> { + if (event.getEntity() instanceof Player player && player.getItemBySlot(EquipmentSlot.CHEST).getItem().equals(Items.DIAMOND_CHESTPLATE)) + event.setNewDamage(EquipmentSlot.CHEST, 5); + }); + + test.onGameTest(helper -> { + DamageSource source = new DamageSource(helper.getLevel().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(DamageTypes.MOB_ATTACK)); + helper.startSequence(() -> helper.makeMockPlayer(GameType.SURVIVAL)) + .thenExecute(player -> player.invulnerableTime = 0) + .thenExecute(player -> player.setItemSlot(EquipmentSlot.CHEST, new ItemStack(Items.DIAMOND_CHESTPLATE))) + .thenExecute(player -> player.hurt(source, 10F)) + .thenWaitUntil(player -> helper.assertTrue(player.getItemBySlot(EquipmentSlot.CHEST).getItem().equals(Items.DIAMOND_CHESTPLATE) + && player.getItemBySlot(EquipmentSlot.CHEST).getDamageValue() == 5, + "Armor hurt not applied. %s actual but expected 5f".formatted(player.getItemBySlot(EquipmentSlot.CHEST).getDamageValue()))) + .thenSucceed(); + }); + } + @GameTest @EmptyTemplate @TestHolder(description = "Tests if the PlayerRespawnPositionEvent fires correctly and can change where the player respawns") From 238c02a243348c26a228f0a8fa60fe48e9d84e48 Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:49:30 +0300 Subject: [PATCH 9/9] Action updates (#1172) --- .github/workflows/build-prs.yml | 22 ++++++--- .github/workflows/check-local-changes.yml | 48 +++++++++++++++++++ .github/workflows/publish-jcc.yml | 20 ++++++++ .github/workflows/test-prs.yml | 18 +++---- build.gradle | 2 +- projects/neoforge/build.gradle | 22 +++++++++ settings.gradle | 2 +- .../resources/data/c/tags/item/tools/bow.json | 4 +- .../resources/reports/registry_order.json | 1 - .../assets/minecraft/models/item/stick.json | 2 +- 10 files changed, 117 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/check-local-changes.yml create mode 100644 .github/workflows/publish-jcc.yml diff --git a/.github/workflows/build-prs.yml b/.github/workflows/build-prs.yml index f74b9ff476..e6f7e26618 100644 --- a/.github/workflows/build-prs.yml +++ b/.github/workflows/build-prs.yml @@ -31,23 +31,31 @@ jobs: run: git switch -C pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.ref }} + - name: Validate wrapper + uses: gradle/actions/wrapper-validation@v3 + - name: Setup JDK 21 uses: actions/setup-java@v2 with: java-version: '21' distribution: 'temurin' - - name: Setup with Gradle - uses: gradle/gradle-build-action@v2 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 with: - arguments: setup cache-read-only: false + - name: Setup with Gradle + run: ./gradlew setup + - name: Build with Gradle - uses: gradle/gradle-build-action@v2 - with: - arguments: assemble checkFormatting - cache-read-only: false + run: ./gradlew assemble checkFormatting + + - name: Run JCC + run: ./gradlew checkJarCompatibility + + - name: Upload JCC + uses: neoforged/action-jar-compatibility/upload@v1 - name: Publish artifacts uses: neoforged/action-pr-publishing/upload@v1 diff --git a/.github/workflows/check-local-changes.yml b/.github/workflows/check-local-changes.yml new file mode 100644 index 0000000000..6a58660482 --- /dev/null +++ b/.github/workflows/check-local-changes.yml @@ -0,0 +1,48 @@ +name: Check PR local changes + +on: + pull_request: + types: + - synchronize + - opened + - ready_for_review + - reopened + +jobs: + check-local-changes: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1000 + fetch-tags: true + + # GradleUtils will append the branch name to the version, + # but for that we need a properly checked out branch + - name: Create branch for commit + run: + git switch -C pr-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.ref }} + + - name: Setup JDK 21 + uses: actions/setup-java@v2 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + with: + cache-read-only: false + + - name: Setup with Gradle + run: ./gradlew setup + + - name: Run datagen with Gradle + run: ./gradlew :neoforge:runData :tests:runData + + - name: Check no local changes are present + run: | + # Print status for easier debugging + git status + if [ -n "$(git status --porcelain)" ]; then exit 1; fi diff --git a/.github/workflows/publish-jcc.yml b/.github/workflows/publish-jcc.yml new file mode 100644 index 0000000000..f4c78789bc --- /dev/null +++ b/.github/workflows/publish-jcc.yml @@ -0,0 +1,20 @@ +# File generated by the GradleUtils `setupGitHubActionsWorkflows` task, avoid modifying it directly +# The template can be found at https://github.com/neoforged/GradleUtils/blob/a65628b0c89dec60b357ce3f8f6bfa62934b8357/src/actionsTemplate/resources/.github/workflows/publish-jcc.yml + +name: Publish PR JCC output + +on: + workflow_run: + workflows: [Build PRs] + types: + - completed + +jobs: + publish-jcc: + if: true + uses: neoforged/actions/.github/workflows/publish-jcc.yml@main + with: + beta_version_pattern: .*-beta.* + secrets: + JCC_GH_APP_ID: ${{ secrets.JCC_GH_APP_ID }} + JCC_GH_APP_KEY: ${{ secrets.JCC_GH_APP_KEY }} diff --git a/.github/workflows/test-prs.yml b/.github/workflows/test-prs.yml index fd33722179..0fff8674c4 100644 --- a/.github/workflows/test-prs.yml +++ b/.github/workflows/test-prs.yml @@ -34,23 +34,19 @@ jobs: java-version: '21' distribution: 'temurin' - - name: Setup with Gradle - uses: gradle/gradle-build-action@v2 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 with: - arguments: setup cache-read-only: false + - name: Setup with Gradle + run: ./gradlew setup + - name: Run game tests with Gradle - uses: gradle/gradle-build-action@v2 - with: - arguments: :tests:runGameTestServer - cache-read-only: false + run: ./gradlew :tests:runGameTestServer - name: Run JUnit tests with Gradle - uses: gradle/gradle-build-action@v2 - with: - arguments: :tests:runUnitTests - cache-read-only: false + run: ./gradlew :tests:runUnitTests - name: Store reports if: failure() diff --git a/build.gradle b/build.gradle index 183cf8203f..0dcce49b15 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ import java.util.regex.Matcher import java.util.regex.Pattern plugins { - id 'net.neoforged.gradleutils' version '3.0.0-alpha.10' apply false + id 'net.neoforged.gradleutils' version '3.0.0-alpha.13' apply false id 'com.diffplug.spotless' version '6.22.0' apply false id 'net.neoforged.licenser' version '0.7.2' id 'neoforge.formatting-conventions' diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 119dcbea8e..8fdcec818e 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -1,6 +1,10 @@ +import net.neoforged.jarcompatibilitychecker.gradle.JCCPlugin +import net.neoforged.jarcompatibilitychecker.gradle.ProvideNeoForgeJarTask + plugins { id 'java-library' id 'maven-publish' + id 'net.neoforged.jarcompatibilitychecker' version '0.1.9' } apply plugin: 'net.neoforged.gradleutils' @@ -18,6 +22,24 @@ dynamicProject { rootProject.layout.projectDirectory.dir('rejects')) } +final checkVersion = JCCPlugin.providePreviousVersion( + project.providers, project.providers.provider({['https://maven.neoforged.net/releases']}), project.providers.provider({'net.neoforged:neoforge'}) +) +final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { + // Use the same jar that the patches were generated against + cleanJar.set(tasks.generateClientBinaryPatches.clean) + maven.set('https://maven.neoforged.net/releases') + artifact.set('net.neoforged:neoforge') + version.set(checkVersion) + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(java_version) + } +} +checkJarCompatibility { + isAPI = true + baseJar = createCompatJar.flatMap { it.output } +} + installerProfile { profile = 'NeoForge' } diff --git a/settings.gradle b/settings.gradle index 1ac035355b..589f0b3ad1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,7 +7,7 @@ pluginManagement { } plugins { - id 'net.neoforged.gradle.platform' version '7.0.142' + id 'net.neoforged.gradle.platform' version '7.0.149' } rootProject.name = rootDir.name diff --git a/src/generated/resources/data/c/tags/item/tools/bow.json b/src/generated/resources/data/c/tags/item/tools/bow.json index d0dad42854..0e7a3eb3a2 100644 --- a/src/generated/resources/data/c/tags/item/tools/bow.json +++ b/src/generated/resources/data/c/tags/item/tools/bow.json @@ -1,8 +1,8 @@ { "values": [ - "minecraft:brush", + "minecraft:bow", { - "id": "#c:tools/brushes", + "id": "#forge:tools/bows", "required": false }, { diff --git a/src/generated/resources/reports/registry_order.json b/src/generated/resources/reports/registry_order.json index c7e0a8ef88..17ea12b63d 100644 --- a/src/generated/resources/reports/registry_order.json +++ b/src/generated/resources/reports/registry_order.json @@ -81,7 +81,6 @@ "neoforge:attachment_types", "neoforge:biome_modifier_serializers", "neoforge:condition_codecs", - "neoforge:display_contexts", "neoforge:entity_data_serializers", "neoforge:fluid_ingredient_type", "neoforge:fluid_type", diff --git a/tests/src/generated/resources/assets/minecraft/models/item/stick.json b/tests/src/generated/resources/assets/minecraft/models/item/stick.json index e3c18581e5..4e9498d758 100644 --- a/tests/src/generated/resources/assets/minecraft/models/item/stick.json +++ b/tests/src/generated/resources/assets/minecraft/models/item/stick.json @@ -1,7 +1,7 @@ { "parent": "minecraft:item/generated", "display": { - "custom_transformtype_test:hanging": { + "neotests:hanging": { "rotation": [ 62, 147,