Add compatibility with Item Adder (#112)

This commit is contained in:
alexcrea 2026-05-17 17:35:59 +02:00 committed by GitHub
commit 36030c598b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 185 additions and 75 deletions

View file

@ -37,6 +37,9 @@ repositories {
// ExcellentEnchants // ExcellentEnchants
maven(url = "https://repo.nightexpressdev.com/releases") maven(url = "https://repo.nightexpressdev.com/releases")
// ItemsAdder
maven(url = "https://maven.devs.beer/")
// for fast stats // for fast stats
maven { maven {
name = "thenextlvlReleases" name = "thenextlvlReleases"
@ -97,6 +100,9 @@ dependencies {
// SuperEnchants // SuperEnchants
compileOnly(files("libs/SuperEnchants-4.6.2-all.jar")) compileOnly(files("libs/SuperEnchants-4.6.2-all.jar"))
// ItemsAdder API
compileOnly("dev.lone:api-itemsadder:4.0.10")
// Include nms // Include nms
implementation(project(":nms:nms-common")) implementation(project(":nms:nms-common"))
implementation(project(":nms:nms-paper")) implementation(project(":nms:nms-paper"))

View file

@ -1,6 +1,5 @@
package xyz.alexcrea.cuanvil.enchant.wrapped; package xyz.alexcrea.cuanvil.enchant.wrapped;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment; import su.nightexpress.excellentenchants.api.enchantment.CustomEnchantment;

View file

@ -5,10 +5,12 @@ import io.delilaheve.CustomAnvil
import net.kyori.adventure.text.Component import net.kyori.adventure.text.Component
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.command.CommandSender
import org.bukkit.entity.HumanEntity import org.bukkit.entity.HumanEntity
import org.bukkit.event.inventory.InventoryClickEvent 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.ItemStack import org.bukkit.inventory.ItemStack
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
@ -46,6 +48,8 @@ object DependencyManager {
var axPlayerWarpsCompatibility: AxPlayerWarpsDependency? = null var axPlayerWarpsCompatibility: AxPlayerWarpsDependency? = null
var itemsAdderCompatibility: ItemsAdderDependency? = null
val genericDependencies = ArrayList<GenericPluginDependency>() val genericDependencies = ArrayList<GenericPluginDependency>()
fun loadDependency() { fun loadDependency() {
@ -98,6 +102,12 @@ object DependencyManager {
axPlayerWarpsCompatibility = AxPlayerWarpsDependency() axPlayerWarpsCompatibility = AxPlayerWarpsDependency()
} }
if (pluginManager.isPluginEnabled("ItemsAdder")){
val dependency = ItemsAdderDependency(pluginManager.getPlugin("ItemsAdder")!!)
itemsAdderCompatibility = dependency
genericDependencies.add(dependency)
}
// "Generic" dependencies // "Generic" dependencies
if (pluginManager.isPluginEnabled("ToolStats")) if (pluginManager.isPluginEnabled("ToolStats"))
genericDependencies.add(ToolStatsDependency(pluginManager.getPlugin("ToolStats")!!)) genericDependencies.add(ToolStatsDependency(pluginManager.getPlugin("ToolStats")!!))
@ -138,27 +148,35 @@ object DependencyManager {
ecoEnchantCompatibility?.handleConfigReload() ecoEnchantCompatibility?.handleConfigReload()
} }
private fun logException(target: CommandSender, e: Exception) {
CustomAnvil.instance.logger.log(
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Finally, warn the player
target.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
}
private fun logExceptionAndClear(target: CommandSender, inventory: Inventory, e: Exception) {
// Just in case to avoid illegal items
inventory.setItem(ANVIL_OUTPUT_SLOT, null)
logException(target, e)
}
// Return true if should bypass (either by a dependency or error) // Return true if should bypass (either by a dependency or error)
// called before immutability test // called before immutability test
fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { fun earlyTryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean {
try { try {
return earlyUnsafeTryEventPreAnvilBypass(event, player) return earlyUnsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) { } catch (e: Exception) {
CustomAnvil.instance.logger.log( logExceptionAndClear(event.view.player, event.inventory, e)
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
return true return true
} }
} }
@ -184,21 +202,7 @@ object DependencyManager {
try { try {
return unsafeTryEventPreAnvilBypass(event, player) return unsafeTryEventPreAnvilBypass(event, player)
} catch (e: Exception) { } catch (e: Exception) {
CustomAnvil.instance.logger.log( logExceptionAndClear(event.view.player, event.inventory, e)
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
return true return true
} }
} }
@ -238,21 +242,7 @@ object DependencyManager {
unsafeTryTreatAnvilResult(treatEvent) unsafeTryTreatAnvilResult(treatEvent)
return treatEvent; return treatEvent;
} catch (e: Exception) { } catch (e: Exception) {
CustomAnvil.instance.logger.log( logExceptionAndClear(event.view.player, event.inventory, e)
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.view.player.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
return null return null
} }
} }
@ -268,21 +258,7 @@ object DependencyManager {
try { try {
return unsafeTryClickAnvilResultBypass(event, inventory) return unsafeTryClickAnvilResultBypass(event, inventory)
} catch (e: Exception) { } catch (e: Exception) {
CustomAnvil.instance.logger.log( logExceptionAndClear(event.view.player, event.inventory, e)
Level.SEVERE,
"Error while trying to handle custom anvil supported plugin: ",
e
)
trackError(e)
// Just in case to avoid illegal items
event.inventory.setItem(ANVIL_OUTPUT_SLOT, null)
// Finally, warn the player, maybe a lot of time but better warn than do nothing
event.whoClicked.sendMessage(
"[" + ChatColor.YELLOW.toString() + "CustomAnvil" + ChatColor.WHITE.toString() + "] " +
ChatColor.RED.toString() + "Error while handling the anvil."
)
return true return true
} }
} }
@ -316,6 +292,23 @@ object DependencyManager {
return bypass return bypass
} }
// Clone item and use plugin specific clone if needed
fun cloneItem(event: PrepareAnvilEvent, item: ItemStack): ItemStack {
try {
return unsafeCloneItem(item)
} catch (e: Exception) {
logException(event.view.player, e)
return item.clone()
}
}
private fun unsafeCloneItem(item: ItemStack): ItemStack {
val cloned = itemsAdderCompatibility?.tryClone(item)
if(cloned != null) return cloned
return item.clone()
}
fun stripLore(item: ItemStack): MutableList<Component?> { fun stripLore(item: ItemStack): MutableList<Component?> {
val dummy = item.clone() val dummy = item.clone()

View file

@ -31,4 +31,8 @@ object EcoItemDependencyUtil {
return ecoi.itemStack return ecoi.itemStack
} }
fun getItems(): List<NamespacedKey> {
return EcoItems.values().map { item -> item.id }
}
} }

View file

@ -0,0 +1,42 @@
package xyz.alexcrea.cuanvil.dependency.plugins
import dev.lone.itemsadder.api.CustomStack
import dev.lone.itemsadder.api.ItemsAdder
import org.bukkit.NamespacedKey
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.Plugin
class ItemsAdderDependency(plugin: Plugin) : GenericPluginDependency(plugin) {
var isLoaded: Boolean = false
get() {
if (field) return true
// We can't be sure the event is registered before being triggered so we need to use this function
field = ItemsAdder.areItemsLoaded()
return field
}
fun tryClone(item: ItemStack): ItemStack? {
if(!isLoaded) return null
val customItem = CustomStack.byItemStack(item) ?: return null
return CustomStack.getInstance(customItem.namespacedID)?.itemStack
}
fun fromKey(key: NamespacedKey): ItemStack? {
if(!isLoaded) return null
return CustomStack.getInstance(key.toString())?.itemStack
}
fun getKey(item: ItemStack) : NamespacedKey? {
if(!isLoaded) return null
val customItem = CustomStack.byItemStack(item) ?: return null
return NamespacedKey.fromString(customItem.namespacedID)
}
fun idsCount(): Set<String> {
return CustomStack.getNamespacedIdsInRegistry()
}
}

View file

@ -6,7 +6,7 @@ import java.util.*
class ExcludeGroup(name: String) : AbstractMaterialGroup(name) { class ExcludeGroup(name: String) : AbstractMaterialGroup(name) {
override fun createDefaultSet(): MutableSet<NamespacedKey> { override fun createDefaultSet(): MutableSet<NamespacedKey> {
return NegativeSet(HashSet()) return NegativeMaterialSet()
} }
private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet() private var includedGroup: MutableSet<AbstractMaterialGroup> = HashSet()

View file

@ -0,0 +1,22 @@
package xyz.alexcrea.cuanvil.group
import org.bukkit.NamespacedKey
import xyz.alexcrea.cuanvil.util.MaterialUtil
import xyz.alexcrea.cuanvil.util.NegativeSet
class NegativeMaterialSet: NegativeSet<NamespacedKey>() {
override fun iterator(): MutableIterator<NamespacedKey> {
val materials = MaterialUtil.getMaterials()
materials.removeIf { negate.contains(it) }
return materials.iterator()
}
override fun isEmpty(): Boolean {
return negate.size >= MaterialUtil.getMaterialCount()
}
override val size get() = MaterialUtil.getMaterialCount() - negate.size
}

View file

@ -155,7 +155,7 @@ class PrepareAnvilListener : Listener {
val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second) val amount = CustomRecipeUtil.getCustomRecipeAmount(recipe, first, second)
val resultItem: ItemStack = recipe.resultItem!!.clone() val resultItem: ItemStack = DependencyManager.cloneItem(event, recipe.resultItem!!)
resultItem.amount *= amount resultItem.amount *= amount
// Maybe add an option on custom craft to ignore/not ignore penalty ?? // Maybe add an option on custom craft to ignore/not ignore penalty ??
@ -179,7 +179,7 @@ class PrepareAnvilListener : Listener {
event: PrepareAnvilEvent, inventory: AnvilInventory, event: PrepareAnvilEvent, inventory: AnvilInventory,
player: HumanEntity, first: ItemStack player: HumanEntity, first: ItemStack
) { ) {
val resultItem = first.clone() val resultItem = DependencyManager.cloneItem(event, first)
var anvilCost = handleRename(resultItem, inventory, player) var anvilCost = handleRename(resultItem, inventory, player)
// Test/stop if nothing changed. // Test/stop if nothing changed.
@ -251,7 +251,7 @@ class PrepareAnvilListener : Listener {
.combineWith(second.findEnchantments(), first, player) .combineWith(second.findEnchantments(), first, player)
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
val resultItem = first.clone() val resultItem = DependencyManager.cloneItem(event, first)
var anvilCost = 0 var anvilCost = 0
if(hasChanged){ if(hasChanged){
resultItem.setEnchantmentsUnsafe(newEnchants) resultItem.setEnchantmentsUnsafe(newEnchants)
@ -307,7 +307,7 @@ class PrepareAnvilListener : Listener {
): Boolean { ): Boolean {
val unitRepairAmount = first.getRepair(second) ?: return false val unitRepairAmount = first.getRepair(second) ?: return false
val resultItem = first.clone() val resultItem = DependencyManager.cloneItem(event, first)
var anvilCost = handleRename(resultItem, inventory, player) var anvilCost = handleRename(resultItem, inventory, player)
val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount) val repairAmount = resultItem.unitRepair(second.amount, unitRepairAmount)

View file

@ -4,6 +4,7 @@ import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.plugins.EcoItemDependencyUtil import xyz.alexcrea.cuanvil.dependency.plugins.EcoItemDependencyUtil
object MaterialUtil { object MaterialUtil {
@ -18,15 +19,19 @@ object MaterialUtil {
return Material.AIR.key == this return Material.AIR.key == this
} }
private val HasEcoItem = Bukkit.getPluginManager().isPluginEnabled("EcoItems")
val ItemStack.customType: NamespacedKey val ItemStack.customType: NamespacedKey
get() { get() {
if(HasEcoItem) { if(DependencyManager.ecoEnchantCompatibility != null) {
val result = EcoItemDependencyUtil.ecoItemNamespace(this) val result = EcoItemDependencyUtil.ecoItemNamespace(this)
if(result != null) return result if(result != null) return result
} }
val itemAdder = DependencyManager.itemsAdderCompatibility
if(itemAdder != null) {
val result = itemAdder.getKey(this)
if (result != null) return result
}
return this.type.key return this.type.key
} }
@ -36,20 +41,32 @@ object MaterialUtil {
} }
fun getMatFromKey(key: NamespacedKey): Material? { fun getMatFromKey(key: NamespacedKey): Material? {
if(HasEcoItem) { if(DependencyManager.ecoEnchantCompatibility != null) {
val result = EcoItemDependencyUtil.ecoItemMaterialFromKey(key) val result = EcoItemDependencyUtil.ecoItemMaterialFromKey(key)
if(result != null) return result if(result != null) return result
} }
val itemAdder = DependencyManager.itemsAdderCompatibility
if(itemAdder != null) {
val result = itemAdder.fromKey(key)
if (result != null) return result.type
}
return bukkitMaterialFromKey(key) return bukkitMaterialFromKey(key)
} }
fun itemFromKey(key: NamespacedKey): ItemStack { fun itemFromKey(key: NamespacedKey): ItemStack {
if(HasEcoItem) { if(DependencyManager.ecoEnchantCompatibility != null) {
val result = EcoItemDependencyUtil.newEcoItemstack(key) val result = EcoItemDependencyUtil.newEcoItemstack(key)
if(result != null) return result if(result != null) return result
} }
val itemAdder = DependencyManager.itemsAdderCompatibility
if(itemAdder != null) {
val result = itemAdder.fromKey(key)
if (result != null) return result
}
return ItemStack(bukkitMaterialFromKey(key)!!) return ItemStack(bukkitMaterialFromKey(key)!!)
} }
@ -57,5 +74,32 @@ object MaterialUtil {
return getMatFromKey(key) != null return getMatFromKey(key) != null
} }
fun getMaterialCount(): Int {
var count = Material.entries.size
if(DependencyManager.ecoEnchantCompatibility != null) {
count += EcoItemDependencyUtil.getItems().size
}
val itemAdder = DependencyManager.itemsAdderCompatibility
if(itemAdder != null) {
count += itemAdder.idsCount().size
}
return count
}
fun getMaterials(): MutableList<NamespacedKey> {
val all = ArrayList(Material.entries.map { it.key })
if(DependencyManager.ecoEnchantCompatibility != null) {
all.addAll(EcoItemDependencyUtil.getItems())
}
val itemAdder = DependencyManager.itemsAdderCompatibility
if(itemAdder != null) {
all.addAll(itemAdder.idsCount().map { NamespacedKey.fromString(it) })
}
return all
}
} }

View file

@ -1,6 +1,6 @@
package xyz.alexcrea.cuanvil.group package xyz.alexcrea.cuanvil.util
class NegativeSet<T>(val negate: MutableSet<T>) : MutableSet<T> { open class NegativeSet<T>(val negate: MutableSet<T> = HashSet()) : MutableSet<T> {
override fun iterator(): MutableIterator<T> { override fun iterator(): MutableIterator<T> {
TODO("Not yet implemented") // can't be implemented I guess TODO("Not yet implemented") // can't be implemented I guess
@ -15,7 +15,7 @@ class NegativeSet<T>(val negate: MutableSet<T>) : MutableSet<T> {
} }
override fun addAll(elements: Collection<T>): Boolean { override fun addAll(elements: Collection<T>): Boolean {
return negate.removeAll(elements) return negate.removeAll(elements.toSet())
} }
override fun removeAll(elements: Collection<T>): Boolean { override fun removeAll(elements: Collection<T>): Boolean {
@ -34,7 +34,7 @@ class NegativeSet<T>(val negate: MutableSet<T>) : MutableSet<T> {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override val size get() = TODO("Not yet implemented") override val size: Int get() = TODO("Not yet implemented")
override fun contains(element: T): Boolean { override fun contains(element: T): Boolean {
return !negate.contains(element) return !negate.contains(element)