new treat anvil event

This commit is contained in:
alexcrea 2026-06-10 14:54:13 +02:00
parent d679cd73f9
commit 2efb6e55e2
Signed by: alexcrea
GPG key ID: E346CD16413450E3
10 changed files with 244 additions and 31 deletions

View file

@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
* Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent} * Most of the time you would likely need {@link CAPreAnvilBypassEvent} or {@link CAEarlyPreAnvilBypassEvent}
* for this event to be useful. * for this event to be useful.
* <p> * <p>
* There is also {@link CATreatAnvilResultEvent} that may be better for some use case. * There is also {@link CATreatAnvilResult2Event} that may be better for some use case.
*/ */
public class CAClickResultBypassEvent extends Event implements Cancellable { public class CAClickResultBypassEvent extends Event implements Cancellable {

View file

@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
* <p> * <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful. * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p> * <p>
* It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent} * It is also recommended that you read about {@link CAPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
* as your use case may be more prone to use theses. * as your use case may be more prone to use theses.
*/ */
public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable { public class CAEarlyPreAnvilBypassEvent extends Event implements Cancellable {

View file

@ -18,7 +18,7 @@ import org.jetbrains.annotations.NotNull;
* <p> * <p>
* You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful. * You should also use {@link CAClickResultBypassEvent} if you want to use this event for something useful.
* <p> * <p>
* It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResultEvent} * It is also recommended that you read about {@link CAEarlyPreAnvilBypassEvent} and {@link CATreatAnvilResult2Event}
* as your use case may be more prone to use theses. * as your use case may be more prone to use theses.
*/ */
public class CAPreAnvilBypassEvent extends Event implements Cancellable { public class CAPreAnvilBypassEvent extends Event implements Cancellable {

View file

@ -0,0 +1,196 @@
package xyz.alexcrea.cuanvil.api.event.listener;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.anvil.AnvilUseType;
import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil;
/**
* Called after custom anvil processed the click on the result on the anvil inventory.
* This event should be used to modify the result of an anvil use.
* <p>
* You may also want to check {@link CAClickResultBypassEvent},
* {@link CAPreAnvilBypassEvent}
* and {@link CAEarlyPreAnvilBypassEvent} for your use case
* <p>
* A null result will cancel this event
*/
@SuppressWarnings("unused")
public class CATreatAnvilResult2Event extends Event {
private static final HandlerList HANDLERS = new HandlerList();
public static HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
@NotNull
private final InventoryView view;
private final AnvilUseType useType;
@Nullable
private final ItemStack left;
@Nullable
private final ItemStack right;
@Nullable
private ItemStack result;
private final AnvilXpUtil.AnvilCost cost;
@ApiStatus.Internal
public CATreatAnvilResult2Event(
@NotNull InventoryView view,
Inventory inv,
AnvilUseType useType,
@Nullable ItemStack result,
AnvilXpUtil.AnvilCost cost) {
this.view = view;
this.useType = useType;
this.left = inv.getItem(0); // TODO use view here
this.right = inv.getItem(1);
this.result = result;
this.cost = cost;
}
/**
* Get the bukkit inventory view.
* <p>
* Temporarily marked as internal as it will get changed to anvil view on legacy removal
* so signature will change
*
* @return The inventory view of this event.
*/
@ApiStatus.Internal
public @NotNull InventoryView getView() {
return view;
}
/**
* Get the type of use source of the result.
*
* @return The craft use type.
*/
public AnvilUseType getUseType() {
return useType;
}
/**
* Get the left item of the anvil use
*
* @return the left item
*/
public @Nullable ItemStack getLeftItem() {
return left;
}
/**
* Get the right item of the anvil use
*
* @return the right item
*/
public @Nullable ItemStack getRightItem() {
return right;
}
/**
* Get the current result
* <p>
* note that it will not be null unless another listener previously set it to null.
*
* @return The current result.
*/
public @Nullable ItemStack getResult() {
return result;
}
/**
* Set the current result
* <p>
* note that a null result will cancel this anvil use.
*
* @param result The new result
*/
public void setResult(@Nullable ItemStack result) {
this.result = result;
}
/**
* Get the level cost displayed on the anvil.
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
* </ul>
*
* @return The current cost.
* @deprecated use #{@link #getCost()} instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public int getLevelCost() {
return cost.asXpCost();
}
/**
* Set the level cost displayed on the anvil.
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
* </ul>
*
* @param levelCost The new cost.
* @deprecated use #{@link #getCost()} and set value on this instead
*/
@Deprecated(forRemoval = true, since = "1.17.0")
public void setLevelCost(int levelCost) {
cost.setGeneric(levelCost - cost.getGeneric() - cost.asXpCost());
}
/**
* Allow access to the current cost of the event
* Note that modifying this object will change the event resulting cost
*
* <h3>Important note:</h3>
* the final price are re calculated on click for the following use case:
* <ul>
* <li>Custom craft</li>
* <li>Unit repair</li>
* <li>Lore edit</li>
* </ul>
* This value will be used as final price for:
* <li>Item merge</li>
* <li>Item rename</li>
*
* @return the current anvil cost
*/
public AnvilXpUtil.AnvilCost getCost() {
return cost;
}
}

View file

@ -19,8 +19,8 @@ import xyz.alexcrea.cuanvil.util.anvil.AnvilXpUtil.AnvilCost;
* <p> * <p>
* A null result will cancel this pre anvil event * A null result will cancel this pre anvil event
* *
* @deprecated Prepare anvil Event should not be provided as it can be called on result and therefor not have prepare anvil event * @deprecated Prepare anvil Event cannot be provided as it can be called on result and therefore not have prepared anvil event
* TODO a replacement is necessary but not yet made * use {@link CATreatAnvilResult2Event} instead
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Deprecated(forRemoval = true, since = "1.17.0") @Deprecated(forRemoval = true, since = "1.17.0")

View file

@ -13,6 +13,7 @@ import org.bukkit.Material
import org.bukkit.entity.HumanEntity import org.bukkit.entity.HumanEntity
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
@ -99,6 +100,7 @@ object AnvilMergeLogic {
} }
fun doRenaming( fun doRenaming(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory, inventory: AnvilInventory,
player: Player, first: ItemStack player: Player, first: ItemStack
): AnvilResult { ): AnvilResult {
@ -113,7 +115,7 @@ object AnvilMergeLogic {
} }
cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY) cost.workPenalty = AnvilXpUtil.calculatePenalty(first, null, resultItem, AnvilUseType.RENAME_ONLY)
val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.RENAME_ONLY, cost) val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.RENAME_ONLY, cost)
return AnvilResult(result, cost) return AnvilResult(result, cost)
} }
@ -183,6 +185,7 @@ object AnvilMergeLogic {
} }
fun doMerge( fun doMerge(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory, inventory: AnvilInventory,
player: Player, player: Player,
first: ItemStack, second: ItemStack first: ItemStack, second: ItemStack
@ -217,7 +220,7 @@ object AnvilMergeLogic {
// Calculate rename cost // Calculate rename cost
cost.rename = handleRename(resultItem, inventory, player) cost.rename = handleRename(resultItem, inventory, player)
val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.MERGE, cost) val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.MERGE, cost)
return AnvilResult(result, cost) return AnvilResult(result, cost)
} }
@ -236,6 +239,8 @@ object AnvilMergeLogic {
// return true if a custom recipe exist with these ingredients // return true if a custom recipe exist with these ingredients
fun testCustomRecipe( fun testCustomRecipe(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory,
player: Player, player: Player,
first: ItemStack, second: ItemStack? first: ItemStack, second: ItemStack?
): CustomCraftResult { ): CustomCraftResult {
@ -256,21 +261,23 @@ object AnvilMergeLogic {
cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost) cost.recipe = if (recipe.removeExactLinearXp) AnvilXpUtil.calculateMinimumLevelForXp(xpCost)
else AnvilXpUtil.calculateLevelForXp(xpCost) else AnvilXpUtil.calculateLevelForXp(xpCost)
val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.CUSTOM_CRAFT, cost) val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.CUSTOM_CRAFT, cost)
return CustomCraftResult(result, cost, amount, recipe) return CustomCraftResult(result, cost, amount, recipe)
} }
fun testUnitRepair( fun testUnitRepair(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory, inventory: AnvilInventory,
player: Player, player: Player,
first: ItemStack, second: ItemStack first: ItemStack, second: ItemStack
): UnitRepairResult { ): UnitRepairResult {
val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY val unitRepairAmount = first.getRepair(second) ?: return UnitRepairResult.EMPTY
return testUnitRepair(inventory, player, first, second, unitRepairAmount) return testUnitRepair(view, inventory, player, first, second, unitRepairAmount)
} }
fun testUnitRepair( fun testUnitRepair(
view: InventoryView, //TODO use anvil view instead
inventory: AnvilInventory, inventory: AnvilInventory,
player: Player, player: Player,
first: ItemStack, second: ItemStack, first: ItemStack, second: ItemStack,
@ -293,7 +300,7 @@ object AnvilMergeLogic {
return UnitRepairResult.EMPTY return UnitRepairResult.EMPTY
} }
val result = DependencyManager.tryTreatAnvilResult(resultItem, AnvilUseType.UNIT_REPAIR, cost) val result = DependencyManager.tryTreatAnvilResult(view, inventory, player, resultItem, AnvilUseType.UNIT_REPAIR, cost)
return UnitRepairResult(result, cost, repairAmount) return UnitRepairResult(result, cost, repairAmount)
} }

View file

@ -12,12 +12,13 @@ import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.Inventory import org.bukkit.inventory.Inventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAClickResultBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAEarlyPreAnvilBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CAPreAnvilBypassEvent import xyz.alexcrea.cuanvil.api.event.listener.CAPreAnvilBypassEvent
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency import xyz.alexcrea.cuanvil.dependency.datapack.DataPackDependency
import xyz.alexcrea.cuanvil.dependency.gui.GenericExternGuiTester import xyz.alexcrea.cuanvil.dependency.gui.GenericExternGuiTester
@ -234,23 +235,24 @@ object DependencyManager {
// Return null if there was an issue // Return null if there was an issue
fun tryTreatAnvilResult( fun tryTreatAnvilResult(
view: InventoryView,
inventory: Inventory, // TODO REMOVE, use view instead on legacy removal
player: HumanEntity,
result: ItemStack, result: ItemStack,
useType: AnvilUseType, useType: AnvilUseType,
cost: AnvilXpUtil.AnvilCost cost: AnvilXpUtil.AnvilCost
): ItemStack? { ): ItemStack? {
//TODO val treatEvent = CATreatAnvilResult2Event(view, inventory, useType, result, cost)
/*val treatEvent = CATreatAnvilResultEvent(event, useType, result, cost)
try { try {
unsafeTryTreatAnvilResult(treatEvent) unsafeTryTreatAnvilResult(treatEvent)
return treatEvent.result return treatEvent.result
} catch (e: Exception) { } catch (e: Exception) {
logExceptionAndClear(event.view.player, event.inventory, e) logExceptionAndClear(player, inventory, e)
return null return null
}*/ }
return result
} }
private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResultEvent) { private fun unsafeTryTreatAnvilResult(event: CATreatAnvilResult2Event) {
Bukkit.getPluginManager().callEvent(event) Bukkit.getPluginManager().callEvent(event)
excellentEnchantsCompatibility?.treatAnvilResult(event) excellentEnchantsCompatibility?.treatAnvilResult(event)

View file

@ -8,7 +8,7 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.api.EnchantmentApi import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResultEvent import xyz.alexcrea.cuanvil.api.event.listener.CATreatAnvilResult2Event
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEPreV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5Enchantment
import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment import xyz.alexcrea.cuanvil.enchant.wrapped.CAEEV5_4Enchantment
@ -218,14 +218,17 @@ class ExcellentEnchantsDependency {
return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean return handleRechargeMethod.invoke(this.usedAnvilListener, event, first, second) as Boolean
} }
fun treatAnvilResult(event: CATreatAnvilResultEvent) { fun treatAnvilResult(event: CATreatAnvilResult2Event) {
val result = event.result val result = event.result
if (result == null) return if (result == null) return
val first: ItemStack = treatInput(event.event.inventory.getItem(0)) val first: ItemStack = treatInput(event.leftItem)
val second: ItemStack = treatInput(event.event.inventory.getItem(1)) val second: ItemStack = treatInput(event.rightItem)
handleCombineMethod.invoke(this.usedAnvilListener, event.event, first, second, result) val fakeEvent = PrepareAnvilEvent(event.view, result)
handleCombineMethod.invoke(this.usedAnvilListener, fakeEvent, first, second, result)
event.result = fakeEvent.result
} }
fun testAnvilResult(event: InventoryClickEvent): Any { fun testAnvilResult(event: InventoryClickEvent): Any {

View file

@ -53,6 +53,7 @@ class AnvilResultListener : Listener {
fun anvilExtractionCheck(event: InventoryClickEvent) { fun anvilExtractionCheck(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return val player = event.whoClicked as? Player ?: return
val inventory = event.inventory as? AnvilInventory ?: return val inventory = event.inventory as? AnvilInventory ?: return
val view = event.view
if (event.rawSlot != ANVIL_OUTPUT_SLOT) { if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return return
@ -74,7 +75,7 @@ class AnvilResultListener : Listener {
} }
// Test custom recipe // Test custom recipe
val customRecipeResult = AnvilMergeLogic.testCustomRecipe(player, leftItem, rightItem) val customRecipeResult = AnvilMergeLogic.testCustomRecipe(view, inventory, player, leftItem, rightItem)
if (!customRecipeResult.isEmpty()) { if (!customRecipeResult.isEmpty()) {
onCustomCraft( onCustomCraft(
event, player, inventory, event, player, inventory,
@ -90,7 +91,7 @@ class AnvilResultListener : Listener {
// Rename // Rename
if (rightItem == null) { if (rightItem == null) {
val result = AnvilMergeLogic.doRenaming(inventory, player, leftItem) val result = AnvilMergeLogic.doRenaming(view, inventory, player, leftItem)
if (result.isEmpty()) return if (result.isEmpty()) return
extractAnvilResult( extractAnvilResult(
@ -105,7 +106,7 @@ class AnvilResultListener : Listener {
// Merge // Merge
val canMerge = leftItem.canMergeWith(rightItem) val canMerge = leftItem.canMergeWith(rightItem)
if (canMerge) { if (canMerge) {
val result = AnvilMergeLogic.doMerge(inventory, player, leftItem, rightItem) val result = AnvilMergeLogic.doMerge(view, inventory, player, leftItem, rightItem)
extractAnvilResult( extractAnvilResult(
event, player, inventory, event, player, inventory,
@ -118,7 +119,7 @@ class AnvilResultListener : Listener {
// Unit repair // Unit repair
val unitRepairResult = AnvilMergeLogic.testUnitRepair( val unitRepairResult = AnvilMergeLogic.testUnitRepair(
inventory, player, view, inventory, player,
leftItem, rightItem leftItem, rightItem
) )
if (unitRepairResult.isEmpty()) { if (unitRepairResult.isEmpty()) {
@ -481,6 +482,8 @@ class AnvilResultListener : Listener {
) { ) {
val paperMeta = rightItem.itemMeta ?: return val paperMeta = rightItem.itemMeta ?: return
val paperCopy: ItemStack? val paperCopy: ItemStack?
if (LoreEditType.APPEND_PAPER.doConsume) { if (LoreEditType.APPEND_PAPER.doConsume) {
paperCopy = null paperCopy = null

View file

@ -11,6 +11,7 @@ import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.EnchantmentStorageMeta import org.bukkit.inventory.meta.EnchantmentStorageMeta
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
@ -91,11 +92,12 @@ class PrepareAnvilListener : Listener {
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val result = getResult(inventory, player, first, second) val result = getResult(view, inventory, player, first, second)
applyResult(event, player, result) applyResult(event, player, result)
} }
fun getResult( fun getResult(
view: InventoryView, //TODO use anvil view
inventory: AnvilInventory, inventory: AnvilInventory,
player: Player, player: Player,
first: ItemStack?, second: ItemStack?) : AnvilResult first: ItemStack?, second: ItemStack?) : AnvilResult
@ -104,7 +106,7 @@ class PrepareAnvilListener : Listener {
return AnvilResult.EMPTY return AnvilResult.EMPTY
// Test custom recipe // Test custom recipe
var result: AnvilResult = testCustomRecipe(player, first, second) var result: AnvilResult = testCustomRecipe(view, inventory, player, first, second)
if (!result.isEmpty()) if (!result.isEmpty())
return result return result
@ -112,14 +114,14 @@ class PrepareAnvilListener : Listener {
val shouldTryRename = second.isAir val shouldTryRename = second.isAir
CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename") CustomAnvil.verboseLog("checking air in main logic: $shouldTryRename")
if (shouldTryRename) if (shouldTryRename)
return doRenaming(inventory, player, first) return doRenaming(view, inventory, player, first)
// Test for merge // Test for merge
if (first.canMergeWith(second!!)) if (first.canMergeWith(second!!))
return doMerge(inventory, player, first, second) return doMerge(view, inventory, player, first, second)
// Test for unit repair // Test for unit repair
result = testUnitRepair(inventory, player, first, second) result = testUnitRepair(view, inventory, player, first, second)
if (!result.isEmpty()) if (!result.isEmpty())
return result return result