25 incompatible with disenchantment (#26)

Ensure compatibility with disenchantment and make it use custom anvil xp
settings.
This commit is contained in:
alexcrea 2024-08-21 16:20:41 +02:00 committed by GitHub
commit 42814237f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 359 additions and 164 deletions

View file

@ -47,18 +47,22 @@ under 1.2.0 replace ca prefix by ue and use ue.unsafe. some permission/features
anvilconfigreload or carl: Reload every config of this plugin
customanvilconfig or configanvil: open a menu for administrator to edit plugin's config in game
```
### Custom Enchantment Plugins
Custom Anvil can be compatible with some custom enchant plugins. \
Currently, there is not a lot of compatible plugin
### Supported Plugins
Custom Anvil can be compatible with some custom enchant and anvil mechanics plugins.
Here is a list of supported plugins with support status:
Here is a list of supported custom enchantment plugins with support status:
- [Enchantment²](https://www.spigotmc.org/resources/enchants-squared-the-enchantsplus-rewrite-custom-enchantments-that-act-like-vanilla-ones.86747/):
Officially supported by Custom Anvil but still experimental. Automatic configuration.
- [EcoEnchant](https://www.spigotmc.org/resources/50-sale-%E2%8C%9B-ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/):
Officially supported by Custom Anvil but still experimental. Need to use /anvilconfigreload or a server restart to add newly added enchantment.
Use EcoEnchant restriction system by default.
If you like Custom Anvil to support a specific custom enchantment plugin.
- [EcoEnchant](https://www.spigotmc.org/resources/ecoenchants-%E2%AD%95-250-enchantments-%E2%9C%85-create-custom-enchants-%E2%9C%A8-essentials-cmi-support.79573/):
Officially supported by Custom Anvil but still experimental. Need to use /anvilconfigreload or a server restart to add newly added enchantment.
Use EcoEnchant restriction system by default.
Here is a list of supported anvil mechanic plugins with support status:
- [Disenchantment](https://www.spigotmc.org/resources/disenchantment-1-21-1-1-20-6-new-book-splitting-mechanics.110741/)
Officially supported by Custom Anvil but still experimental. mostly use Custom Anvil xp settings.
If you like Custom Anvil to support a specific plugin (custom enchant or anvil mechanic).
You can ask, but please note implementing compatibility will be considered
as low priority as I work for the plugin on my free time for free.

View file

@ -14,7 +14,7 @@ plugins {
}
group = "xyz.alexcrea"
version = "1.5.5"
version = "1.6.0"
repositories {
// EcoEnchants
@ -36,6 +36,9 @@ dependencies {
compileOnly("com.willfp:EcoEnchants:12.5.1")
compileOnly("com.willfp:eco:6.70.1")
// Disenchantment
compileOnly("cz.kominekjan:Disenchantment:v5.3.1")
// Include nms
implementation(project(":nms:nms-common"))
implementation(project(":nms:v1_17R1", configuration = "reobf"))

View file

@ -1,5 +1,14 @@
import java.net.URI
rootProject.name = "CustomAnvil"
// for Disenchantment dependency
sourceControl {
gitRepository(URI.create("https://github.com/H7KZ/Disenchantment.git")) {
producesModule("cz.kominekjan:Disenchantment")
}
}
// NMS subproject
include("nms:nms-common")
findProject(":nms:nms-common")?.name = "nms-common"

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.enchant.wrapped;
import me.athlaeos.enchantssquared.enchantments.CustomEnchant;
import me.athlaeos.enchantssquared.managers.CustomEnchantManager;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;

View file

@ -9,7 +9,8 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import xyz.alexcrea.cuanvil.gui.config.MainConfigGui;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
public class GuiSharedConstant {

View file

@ -2,7 +2,6 @@ package io.delilaheve
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.EnchantmentUtil.enchantmentName
import io.delilaheve.util.ItemUtil.canMergeWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
@ -25,11 +24,13 @@ import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView.Property.REPAIR_COST
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Repairable
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.group.ConflictType
import xyz.alexcrea.cuanvil.recipe.AnvilCustomRecipe
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.calculatePenalty
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.getRightValues
import xyz.alexcrea.cuanvil.util.AnvilXpUtil.setAnvilInvXp
import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair
import java.util.regex.Matcher
import java.util.regex.Pattern
@ -43,9 +44,9 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
companion object {
// Anvil's output slot
private const val ANVIL_INPUT_LEFT = 0
private const val ANVIL_INPUT_RIGHT = 1
private const val ANVIL_OUTPUT_SLOT = 2
const val ANVIL_INPUT_LEFT = 0
const val ANVIL_INPUT_RIGHT = 1
const val ANVIL_OUTPUT_SLOT = 2
// static slot container
private val NO_SLOT = SlotContainer(SlotType.NO_SLOT, 0)
@ -57,6 +58,9 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
*/
@EventHandler(priority = HIGHEST)
fun anvilCombineCheck(event: PrepareAnvilEvent) {
// Test if the event should bypass custom anvil.
if(DependencyManager.tryEventPreAnvilBypass(event)) return
val inventory = event.inventory
val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val second = inventory.getItem(ANVIL_INPUT_RIGHT)
@ -75,7 +79,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
resultItem.amount *= amount
event.result = resultItem
handleAnvilXp(inventory, event, recipe.xpCostPerCraft * amount, true)
setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true)
return
}
@ -96,7 +100,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
anvilCost += calculatePenalty(first, null, resultItem)
handleAnvilXp(inventory, event, anvilCost)
setAnvilInvXp(inventory, event.view, anvilCost)
return
}
@ -130,7 +134,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
// Finally, we set result
event.result = resultItem
handleAnvilXp(inventory, event, anvilCost)
setAnvilInvXp(inventory, event.view, anvilCost)
return
}
@ -155,7 +159,7 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
}
event.result = resultItem
handleAnvilXp(inventory, event, anvilCost)
setAnvilInvXp(inventory, event.view, anvilCost)
} else {
CustomAnvil.log("no anvil fuse type found")
event.result = null
@ -282,9 +286,13 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
val player = event.whoClicked as? Player ?: return
if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return
val inventory = event.inventory as? AnvilInventory ?: return
if (event.rawSlot != ANVIL_OUTPUT_SLOT) {
return
}
// Test if the event should bypass custom anvil.
if(DependencyManager.tryClickAnvilResultBypass(event, inventory)) return
val output = inventory.getItem(ANVIL_OUTPUT_SLOT) ?: return
val leftItem = inventory.getItem(ANVIL_INPUT_LEFT) ?: return
val rightItem = inventory.getItem(ANVIL_INPUT_RIGHT)
@ -504,96 +512,6 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
}
}
/**
* Function to calculate work penalty of anvil work
* Also change result work penalty if right item is not null
*/
private fun calculatePenalty(left: ItemStack, right: ItemStack?, result: ItemStack): Int {
return calculatePenalty(left, right, result, false)
}
/**
* Function to calculate work penalty of anvil work
* Also change result work penalty if right item is not null
*/
private fun calculatePenalty(left: ItemStack, right: ItemStack?, result: ItemStack, unitRepair: Boolean): Int {
// Extracted From https://minecraft.fandom.com/wiki/Anvil_mechanics#Enchantment_equation
// Calculate work penalty
val leftPenalty = (left.itemMeta as? Repairable)?.repairCost ?: 0
val rightPenalty =
if (right == null) {
0
} else {
(right.itemMeta as? Repairable)?.repairCost ?: 0
}
// Increase penalty on fusing or unit repair
if(right != null || unitRepair){
result.itemMeta?.let {
(it as? Repairable)?.repairCost = leftPenalty * 2 + 1
result.itemMeta = it
}
}
CustomAnvil.log(
"Calculated penalty: " +
"leftPenalty: $leftPenalty, " +
"rightPenalty: $rightPenalty, " +
"result penalty: ${(result.itemMeta as? Repairable)?.repairCost ?: "none"}"
)
return leftPenalty + rightPenalty
}
/**
* Function to calculate right enchantment values
* it include enchantment placed on final item and conflicting enchantment
*/
private fun getRightValues(right: ItemStack, result: ItemStack): Int {
// Calculate right value and illegal enchant penalty
var illegalPenalty = 0
var rightValue = 0
val rightIsFormBook = right.isEnchantedBook()
val resultEnchs = result.findEnchantments()
val resultEnchsKeys = HashMap(resultEnchs)
for (enchantment in right.findEnchantments()) {
// count enchant as illegal enchant if it conflicts with another enchant or not in result
if ((enchantment.key !in resultEnchsKeys)) {
resultEnchsKeys[enchantment.key] = enchantment.value
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(
resultEnchsKeys,
result,
enchantment.key
)
resultEnchsKeys.remove(enchantment.key)
if (ConflictType.ENCHANTMENT_CONFLICT == conflictType) {
illegalPenalty += ConfigOptions.sacrificeIllegalCost
CustomAnvil.verboseLog("Big conflict. Adding illegal price penalty")
}
continue
}
// We know "enchantment.key in resultEnchs" true
val resultLevel = resultEnchs[enchantment.key]!!
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook)
val value = resultLevel * enchantmentMultiplier
CustomAnvil.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
rightValue += value
}
CustomAnvil.log(
"Calculated right values: " +
"rightValue: $rightValue, " +
"illegalPenalty: $illegalPenalty"
)
return rightValue + illegalPenalty
}
private fun getCustomRecipe (
leftItem: ItemStack,
rightItem: ItemStack?) : AnvilCustomRecipe? {
@ -638,58 +556,6 @@ class AnvilEventListener(private val packetManager: PacketManager) : Listener {
}
/**
* Display xp needed for the work on the anvil inventory
*/
private fun handleAnvilXp(
inventory: AnvilInventory,
event: PrepareAnvilEvent,
anvilCost: Int,
ignoreRules: Boolean = false
) {
// Test repair cost limit
val finalAnvilCost = if (
!ignoreRules &&
!ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost) {
min(anvilCost, ConfigOptions.maxAnvilCost)
} else {
anvilCost
}
/* Because Minecraft likes to have the final say in the repair cost displayed
* we need to wait for the event to end before overriding it, this ensures that
* we have the final say in the process. */
CustomAnvil.instance
.server
.scheduler
.runTask(CustomAnvil.instance, Runnable {
inventory.maximumRepairCost =
if (ConfigOptions.doRemoveCostLimit || ignoreRules)
{ Int.MAX_VALUE }
else
{ ConfigOptions.maxAnvilCost + 1 }
val player = event.view.player
inventory.repairCost = finalAnvilCost
event.view.setProperty(REPAIR_COST, finalAnvilCost)
player.openInventory.setProperty(REPAIR_COST, finalAnvilCost)
if(player is Player){
if(player.gameMode != GameMode.CREATIVE ){
val bypassToExpensive = (ConfigOptions.doReplaceTooExpensive) &&
(finalAnvilCost >= 40) &&
finalAnvilCost < inventory.maximumRepairCost
packetManager.setInstantBuild(player, bypassToExpensive)
}
player.updateInventory()
}
})
}
@EventHandler
fun onAnvilClose(event: InventoryCloseEvent){
val player = event.player

View file

@ -1,6 +1,9 @@
package xyz.alexcrea.cuanvil.dependency
import org.bukkit.Bukkit
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.packet.PacketManager
import xyz.alexcrea.cuanvil.dependency.packet.PacketManagerSelector
@ -10,6 +13,7 @@ object DependencyManager {
lateinit var packetManager: PacketManager
var enchantmentSquaredCompatibility: EnchantmentSquaredDependency? = null
var ecoEnchantCompatibility: EcoEnchantDependency? = null
var disenchantmentCompatibility: DisenchantmentDependency? = null
fun loadDependency(){
val pluginManager = Bukkit.getPluginManager()
@ -30,6 +34,12 @@ object DependencyManager {
ecoEnchantCompatibility!!.disableAnvilListener()
}
// Disenchantment dependency
if(pluginManager.isPluginEnabled("Disenchantment")){
disenchantmentCompatibility = DisenchantmentDependency()
disenchantmentCompatibility!!.redirectListeners()
}
}
fun handleCompatibilityConfig() {
@ -52,4 +62,20 @@ object DependencyManager {
}
fun tryEventPreAnvilBypass(event: PrepareAnvilEvent): Boolean {
var bypass = false
if(disenchantmentCompatibility?.testPrepareAnvil(event) == true) bypass = true
return bypass
}
fun tryClickAnvilResultBypass(event: InventoryClickEvent, inventory: AnvilInventory): Boolean {
var bypass = false
if(disenchantmentCompatibility?.testAnvilResult(event, inventory) == true) bypass = true
return bypass
}
}

View file

@ -0,0 +1,123 @@
package xyz.alexcrea.cuanvil.dependency
import cz.kominekjan.disenchantment.events.ItemClickEvent
import cz.kominekjan.disenchantment.events.ItemEvent
import cz.kominekjan.disenchantment.events.SplitBookClickEvent
import cz.kominekjan.disenchantment.events.SplitBookEvent
import io.delilaheve.AnvilEventListener
import io.delilaheve.CustomAnvil
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.ItemStack
import org.bukkit.plugin.RegisteredListener
import xyz.alexcrea.cuanvil.util.AnvilXpUtil
class DisenchantmentDependency {
init {
CustomAnvil.instance.logger.info("Disenchantment Detected !")
}
private lateinit var splitEvent: SplitBookEvent
private lateinit var itemEvent: ItemEvent
private lateinit var splitBookClickEvent: SplitBookClickEvent
private lateinit var itemClickEvent: ItemClickEvent
fun redirectListeners() {
val toUnregister = ArrayList<RegisteredListener>()
// get required PrepareAnvilEvent listener
for (registeredListener in PrepareAnvilEvent.getHandlerList().registeredListeners) {
val listener = registeredListener.listener
if(listener is SplitBookEvent){
this.splitEvent = listener
toUnregister.add(registeredListener)
}
if(listener is ItemEvent){
itemEvent = listener
toUnregister.add(registeredListener)
}
}
for (listener in toUnregister) {
PrepareAnvilEvent.getHandlerList().unregister(listener)
}
toUnregister.clear()
// get required InventoryClickEvent listener
for (registeredListener in InventoryClickEvent.getHandlerList().registeredListeners) {
val listener = registeredListener.listener
if(listener is SplitBookClickEvent){
splitBookClickEvent = listener
toUnregister.add(registeredListener)
}
if(listener is ItemClickEvent){
itemClickEvent = listener
toUnregister.add(registeredListener)
}
}
for (listener in toUnregister) {
InventoryClickEvent.getHandlerList().unregister(listener)
}
}
fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean {
val previousResult = event.result
event.result = null
// Test if event change the result
itemEvent.onDisenchantmentEvent(event)
if(event.result != null) {
CustomAnvil.log("Detected pre anvil item extract bypass.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost)
return true
}
splitEvent.onDisenchantmentEvent(event)
if(event.result != null) {
CustomAnvil.log("Detected pre anvil split enchant bypass.")
AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost)
return true
}
event.result = previousResult
return false
}
fun testAnvilResult(event: InventoryClickEvent, inventory: AnvilInventory): Boolean {
val previousResultSlot = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)?.clone()
// Test event if change the result
itemClickEvent.onDisenchantmentClickEvent(event)
if(!testAnvilInventoryChange(inventory, previousResultSlot) || event.isCancelled) {
CustomAnvil.log("Detected anvil click item extract bypass.")
return true
}
splitBookClickEvent.onDisenchantmentClickEvent(event)
if(!testAnvilInventoryChange(inventory, previousResultSlot) || event.isCancelled) {
CustomAnvil.log("Detected anvil click split enchant bypass.")
return true
}
return false
}
private fun testAnvilInventoryChange(inventory: AnvilInventory, previous: ItemStack?): Boolean {
val currentResult = inventory.getItem(AnvilEventListener.ANVIL_OUTPUT_SLOT)
return currentResult == previous
}
}

View file

@ -0,0 +1,163 @@
package xyz.alexcrea.cuanvil.util
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.enchantmentName
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
import org.bukkit.GameMode
import org.bukkit.entity.Player
import org.bukkit.inventory.AnvilInventory
import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.InventoryView.Property.REPAIR_COST
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Repairable
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.group.ConflictType
import kotlin.math.min
object AnvilXpUtil {
/**
* Display xp needed for the work on the anvil inventory
*/
fun setAnvilInvXp(
inventory: AnvilInventory,
view: InventoryView,
anvilCost: Int,
ignoreRules: Boolean = false
) {
// Test repair cost limit
val finalAnvilCost = if (
!ignoreRules &&
!ConfigOptions.doRemoveCostLimit &&
ConfigOptions.doCapCost) {
min(anvilCost, ConfigOptions.maxAnvilCost)
} else {
anvilCost
}
/* Because Minecraft likes to have the final say in the repair cost displayed
* we need to wait for the event to end before overriding it, this ensures that
* we have the final say in the process. */
CustomAnvil.instance
.server
.scheduler
.runTask(CustomAnvil.instance, Runnable {
inventory.maximumRepairCost =
if (ConfigOptions.doRemoveCostLimit || ignoreRules)
{ Int.MAX_VALUE }
else
{ ConfigOptions.maxAnvilCost + 1 }
val player = view.player
inventory.repairCost = finalAnvilCost
view.setProperty(REPAIR_COST, finalAnvilCost)
player.openInventory.setProperty(REPAIR_COST, finalAnvilCost)
if(player is Player){
if(player.gameMode != GameMode.CREATIVE ){
val bypassToExpensive = (ConfigOptions.doReplaceTooExpensive) &&
(finalAnvilCost >= 40) &&
finalAnvilCost < inventory.maximumRepairCost
DependencyManager.packetManager.setInstantBuild(player, bypassToExpensive)
}
player.updateInventory()
}
})
}
/**
* Function to calculate work penalty of anvil work
* Also change result work penalty if right item is not null
*/
fun calculatePenalty(left: ItemStack, right: ItemStack?, result: ItemStack): Int {
return calculatePenalty(left, right, result, false)
}
/**
* Function to calculate work penalty of anvil work
* Also change result work penalty if right item is not null
*/
fun calculatePenalty(left: ItemStack, right: ItemStack?, result: ItemStack, unitRepair: Boolean): Int {
// Extracted From https://minecraft.fandom.com/wiki/Anvil_mechanics#Enchantment_equation
// Calculate work penalty
val leftPenalty = (left.itemMeta as? Repairable)?.repairCost ?: 0
val rightPenalty =
if (right == null) {
0
} else {
(right.itemMeta as? Repairable)?.repairCost ?: 0
}
// Increase penalty on fusing or unit repair
if(right != null || unitRepair){
result.itemMeta?.let {
(it as? Repairable)?.repairCost = leftPenalty * 2 + 1
result.itemMeta = it
}
}
CustomAnvil.log(
"Calculated penalty: " +
"leftPenalty: $leftPenalty, " +
"rightPenalty: $rightPenalty, " +
"result penalty: ${(result.itemMeta as? Repairable)?.repairCost ?: "none"}"
)
return leftPenalty + rightPenalty
}
/**
* Function to calculate right enchantment values
* it include enchantment placed on final item and conflicting enchantment
*/
fun getRightValues(right: ItemStack, result: ItemStack): Int {
// Calculate right value and illegal enchant penalty
var illegalPenalty = 0
var rightValue = 0
val rightIsFormBook = right.isEnchantedBook()
val resultEnchs = result.findEnchantments()
val resultEnchsKeys = HashMap(resultEnchs)
for (enchantment in right.findEnchantments()) {
// count enchant as illegal enchant if it conflicts with another enchant or not in result
if ((enchantment.key !in resultEnchsKeys)) {
resultEnchsKeys[enchantment.key] = enchantment.value
val conflictType = ConfigHolder.CONFLICT_HOLDER.conflictManager.isConflicting(
resultEnchsKeys,
result,
enchantment.key
)
resultEnchsKeys.remove(enchantment.key)
if (ConflictType.ENCHANTMENT_CONFLICT == conflictType) {
illegalPenalty += ConfigOptions.sacrificeIllegalCost
CustomAnvil.verboseLog("Big conflict. Adding illegal price penalty")
}
continue
}
// We know "enchantment.key in resultEnchs" true
val resultLevel = resultEnchs[enchantment.key]!!
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook)
val value = resultLevel * enchantmentMultiplier
CustomAnvil.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
rightValue += value
}
CustomAnvil.log(
"Calculated right values: " +
"rightValue: $rightValue, " +
"illegalPenalty: $illegalPenalty"
)
return rightValue + illegalPenalty
}
}

View file

@ -1,7 +1,7 @@
main: io.delilaheve.CustomAnvil
name: CustomAnvil
prefix: "Custom Anvil"
version: 1.5.5
version: 1.6.0
description: Allow to customise anvil mechanics
api-version: 1.16
load: POSTWORLD
@ -53,6 +53,7 @@ permissions:
softdepend:
- UnsafeEnchantsPlus
- ProtocolLib
- Disenchantment
- EnchantsSquared
- EcoEnchants
- eco