Compare commits

..

12 commits

Author SHA1 Message Date
896b1db0b8
prepare for v2 2026-06-30 07:50:02 +02:00
4ef033c230
rebase on new update 2026-06-30 07:49:39 +02:00
6d11a2db0e
1.21 cleanup and fix
also random cleanup
2026-06-30 07:49:37 +02:00
06026ef93d
fix from rebase 2026-06-30 07:49:19 +02:00
5a86f13c3f
progress on 1.21 transition 2026-06-30 07:48:28 +02:00
fe346ff4d3
initial 1.21 transition 2026-06-30 07:48:21 +02:00
1e2acb6f48
expose more enchantment function to API & use them
Some checks are pending
Java CI with Gradle / build (push) Waiting to run
2026-06-30 07:47:41 +02:00
72a7860d93
mark superenchant as bulk and clear optimized 2026-06-30 03:44:37 +02:00
447859848b
multiples config related add
Some checks are pending
Java CI with Gradle / build (push) Waiting to run
- Disable console debug by default
- Display log on get to console and not "click to copy"
2026-06-29 13:40:03 +02:00
7ea708ec14
add left item enchantment cost toggle
Some checks are pending
Java CI with Gradle / build (push) Waiting to run
2026-06-29 13:08:50 +02:00
965ee33325
add debug log command
Some checks are pending
Java CI with Gradle / build (push) Waiting to run
2026-06-29 00:56:54 +02:00
fcbe6ba3fb
add more debug & fix price not displaying after fail 2026-06-28 23:29:49 +02:00
20 changed files with 400 additions and 99 deletions

View file

@ -119,6 +119,9 @@ permission_needed_for_dialog_rename: false
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Also count left item enchantments for the final price
include_left_enchantment_for_cost: false
# Override limits for specific enchants
#
# Enchantments not listed here will use the value of default_limit
@ -175,8 +178,8 @@ enchant_limits:
# Multipliers used to calculate the enchantment's value in repair/combining
#
# Values here are pulled from the fandom wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments
# Values here are pulled from the minecraft wiki:
# https://minecraft.wiki/w/Anvil_mechanics#Costs_for_combining_enchantments
#
# If an enchantment is missing values here, or is less than 0, it will default to 0
#
@ -483,5 +486,8 @@ debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# Whether to show inside the console or require to run /ca debug get
display_debug_in_console: false
configVersion: 1.15.5
lowMinecraftVersion: 1.21.11

View file

@ -117,6 +117,9 @@ permission_needed_for_dialog_rename: false
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Also count left item enchantments for the final price
include_left_enchantment_for_cost: false
# Override limits for specific enchants
#
# Enchantments not listed here will use the value of default_limit
@ -172,8 +175,8 @@ enchant_limits:
# Multipliers used to calculate the enchantment's value in repair/combining
#
# Values here are pulled from the fandom wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments
# Values here are pulled from the minecraft wiki:
# https://minecraft.wiki/w/Anvil_mechanics#Costs_for_combining_enchantments
#
# If an enchantment is missing values here, or is less than 0, it will default to 0
#
@ -475,5 +478,8 @@ debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# Whether to show inside the console or require to run /ca debug get
display_debug_in_console: false
configVersion: 1.11.0
lowMinecraftVersion: 1.21.9

View file

@ -117,6 +117,9 @@ permission_needed_for_dialog_rename: false
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Also count left item enchantments for the final price
include_left_enchantment_for_cost: false
# Override limits for specific enchants
#
# Enchantments not listed here will use the value of default_limit
@ -169,8 +172,8 @@ enchant_limits:
# Multipliers used to calculate the enchantment's value in repair/combining
#
# Values here are pulled from the fandom wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments
# Values here are pulled from the minecraft wiki:
# https://minecraft.wiki/w/Anvil_mechanics#Costs_for_combining_enchantments
#
# If an enchantment is missing values here, or is less than 0, it will default to 0
#
@ -463,4 +466,7 @@ debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# Whether to show inside the console or require to run /ca debug get
display_debug_in_console: false
configVersion: 1.11.0

View file

@ -5,6 +5,7 @@ import io.delilaheve.util.ConfigOptions;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder;
@ -38,14 +39,14 @@ public class EnchantmentApi {
* @param enchantment The enchantment to register
* @return True if successful.
*/
public static boolean registerEnchantment(@NotNull CAEnchantment enchantment){
if(!CAEnchantmentRegistry.getInstance().register(enchantment)) return false;
public static boolean registerEnchantment(@NotNull CAEnchantment enchantment) {
if (!CAEnchantmentRegistry.getInstance().register(enchantment)) return false;
// Add enchantment to gui.
if(EnchantCostConfigGui.getInstance() != null){
if (EnchantCostConfigGui.getInstance() != null) {
EnchantCostConfigGui.getInstance().updateValueForGeneric(enchantment, true);
}
if(EnchantLimitConfigGui.getInstance() != null){
if (EnchantLimitConfigGui.getInstance() != null) {
EnchantLimitConfigGui.getInstance().updateValueForGeneric(enchantment, true);
}
@ -62,8 +63,8 @@ public class EnchantmentApi {
* @param defaultRarity The default rarity of the provided enchantment
* @return True if successful.
*/
public static boolean registerEnchantment(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity defaultRarity){
if(defaultRarity == null)
public static boolean registerEnchantment(@NotNull Enchantment enchantment, @Nullable EnchantmentRarity defaultRarity) {
if (defaultRarity == null)
return registerEnchantment(new CABukkitEnchantment(enchantment));
return registerEnchantment(new CABukkitEnchantment(enchantment, defaultRarity));
@ -77,7 +78,7 @@ public class EnchantmentApi {
* @param enchantment The enchantment to register
* @return True if successful.
*/
public static boolean registerEnchantment(@NotNull Enchantment enchantment){
public static boolean registerEnchantment(@NotNull Enchantment enchantment) {
return registerEnchantment(new CABukkitEnchantment(enchantment));
}
@ -87,12 +88,12 @@ public class EnchantmentApi {
* @param enchantment The enchantment to unregister
* @return True if successful.
*/
public static boolean unregisterEnchantment(@Nullable CAEnchantment enchantment){
public static boolean unregisterEnchantment(@Nullable CAEnchantment enchantment) {
// Remove from gui
if(EnchantCostConfigGui.getInstance() != null){
if (EnchantCostConfigGui.getInstance() != null) {
EnchantCostConfigGui.getInstance().removeGeneric(enchantment);
}
if(EnchantLimitConfigGui.getInstance() != null){
if (EnchantLimitConfigGui.getInstance() != null) {
EnchantLimitConfigGui.getInstance().removeGeneric(enchantment);
}
@ -105,7 +106,7 @@ public class EnchantmentApi {
* @param key The enchantment key to unregister
* @return True if successful.
*/
public static boolean unregisterEnchantment(@NotNull NamespacedKey key){
public static boolean unregisterEnchantment(@NotNull NamespacedKey key) {
CAEnchantment enchantment = CAEnchantment.getByKey(key);
return unregisterEnchantment(enchantment);
}
@ -116,7 +117,7 @@ public class EnchantmentApi {
* @param enchantment The enchantment to unregister
* @return True if successful.
*/
public static boolean unregisterEnchantment(@NotNull Enchantment enchantment){
public static boolean unregisterEnchantment(@NotNull Enchantment enchantment) {
return unregisterEnchantment(enchantment.getKey());
}
@ -127,7 +128,7 @@ public class EnchantmentApi {
* @return The custom anvil enchantment of this key. null if not found.
*/
@Nullable
public static CAEnchantment getByKey(@NotNull NamespacedKey key){
public static CAEnchantment getByKey(@NotNull NamespacedKey key) {
return CAEnchantment.getByKey(key);
}
@ -137,29 +138,31 @@ public class EnchantmentApi {
* @param name The name used to fetch
* @return List of custom anvil enchantments of this name. May be empty if not found.
*/
public static List<CAEnchantment> getByName(@NotNull String name){
public static List<CAEnchantment> getByName(@NotNull String name) {
return CAEnchantment.getByName(name);
}
/**
* Get every registered custom anvil enchantments.
*
* @return An immutable map of enchantment key as map key and custom anvil enchantment as value.
*/
@NotNull
public static Map<NamespacedKey, CAEnchantment> getRegisteredEnchantments(){
public static Map<NamespacedKey, CAEnchantment> getRegisteredEnchantments() {
return Collections.unmodifiableMap(CAEnchantmentRegistry.getInstance().registeredEnchantments());
}
/**
* Write the default level and rarity configuration of the enchantment.
*
* @param enchantment The enchantment to write default configuration
* @param override If it should override old configuration
* @param override If it should override old configuration
* @return Return false if override is false and a configuration exist. true otherwise.
*/
public static boolean writeDefaultConfig(CAEnchantment enchantment, boolean override){
public static boolean writeDefaultConfig(CAEnchantment enchantment, boolean override) {
FileConfiguration config = ConfigHolder.DEFAULT_CONFIG.getConfig();
if(tryWriteDefaultConfig(config, enchantment, override)){
if (tryWriteDefaultConfig(config, enchantment, override)) {
prepareSaveTask();
}
return true;
@ -169,7 +172,7 @@ public class EnchantmentApi {
boolean hasChange = false;
String levelPath = ConfigOptions.ENCHANT_LIMIT_ROOT + "." + enchantment.getKey();
if(override || !defaultConfig.isSet(levelPath)){
if (override || !defaultConfig.isSet(levelPath)) {
defaultConfig.set(levelPath, enchantment.defaultMaxLevel());
hasChange = true;
}
@ -179,11 +182,11 @@ public class EnchantmentApi {
String itemPath = basePath + ".item";
String bookPath = basePath + ".book";
if(override || !defaultConfig.isSet(itemPath)){
if (override || !defaultConfig.isSet(itemPath)) {
defaultConfig.set(itemPath, rarity.getItemValue());
hasChange = true;
}
if(override || !defaultConfig.isSet(bookPath)){
if (override || !defaultConfig.isSet(bookPath)) {
defaultConfig.set(bookPath, rarity.getBookValue());
hasChange = true;
}
@ -195,28 +198,81 @@ public class EnchantmentApi {
* Prepare a task to save custom recipe configuration.
*/
private static void prepareSaveTask() {
if(saveChangeTask != null) return;
if (saveChangeTask != null) return;
saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, ()->{
saveChangeTask = DependencyManager.scheduler.scheduleGlobally(CustomAnvil.instance, () -> {
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);
saveChangeTask = null;
});
}
/**
* Add a bulk get operator.
* Add a bulk get operator. (not needed for proper "bukkit" enchantments)
* <p>
* Do not forget to mark your enchantments as {@link CAEnchantment#isGetOptimised() Get Optimized}
*
* @param operation An optimised get enchantments operation
*/
public static void addBulkGet(@NotNull BulkGetEnchantOperation operation){
public static void addBulkGet(@NotNull BulkGetEnchantOperation operation) {
CAEnchantmentRegistry.getInstance().getOptimisedGetOperators().add(operation);
}
/**
* Add a bulk clean operator.
* @param operation An optimised clean enchantments operation
*
* @param operation An optimised clean enchantments operation (not needed for proper "bukkit" enchantments)
* <p>
* Do not forget to mark your enchantments as {@link CAEnchantment#isCleanOptimised() Clean Optimized}
*/
public static void addBulkClean(@NotNull BulkCleanEnchantOperation operation){
public static void addBulkClean(@NotNull BulkCleanEnchantOperation operation) {
CAEnchantmentRegistry.getInstance().getOptimisedCleanOperators().add(operation);
}
/**
* Get all the enchantments of an item
*
* @param item The item to get the enchantment from
* @return A map of key of enchantment, value the level of all the enchantments of the item
* @since 1.17.6
*/
public static Map<CAEnchantment, Integer> getEnchantments(@NotNull ItemStack item) {
return CAEnchantment.getEnchants(item);
}
/**
* Set all the enchantments to an item. Clearing previous enchantments
*
* @param item The item to get the enchantment from
* @param enchants A map of key of enchantment, value the level of all the enchantments
* @since 1.17.6
*/
public static void setEnchantments(@NotNull ItemStack item, @NotNull Map<CAEnchantment, Integer> enchants) {
clearEnchantments(item);
addEnchantments(item, enchants);
}
/**
* Add enchantment with there respective level.
* If the enchantment is already present it will be overridden to the new level
*
* @param item The item to get the enchantment from
* @param enchants A map of key of enchantment, value the level of the new enchantments
* @since 1.17.6
*/
public static void addEnchantments(@NotNull ItemStack item, @NotNull Map<CAEnchantment, Integer> enchants) {
enchants.forEach((enchantment, level) ->
enchantment.addEnchantmentUnsafe(item, level)
);
}
/**
* Clear all the enchantments
*
* @param item The item to clear the enchantments from
* @since 1.17.6
*/
public static void clearEnchantments(@NotNull ItemStack item) {
CAEnchantment.clearEnchants(item);
}
}

View file

@ -41,7 +41,7 @@ public class SuperEnchantBulkOperation implements BulkGetEnchantOperation, BulkC
@Override
public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) {
// item meta is not preferred for enchantment squared clear
// item meta is not preferred for super enchant
}
}

View file

@ -73,4 +73,14 @@ public class CASuperEnchantEnchantment extends CAEnchantmentBase implements Addi
return !enchant.canApplyTo(item.getType());
}
@Override
public boolean isCleanOptimised() {
return true;
}
@Override
public boolean isGetOptimised() {
return true;
}
}

View file

@ -66,6 +66,12 @@ public class PluginSetDefault {
nbSet += trySetDefault(config, DIALOG_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION);
nbSet += trySetDefault(config, DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT);
nbSet += trySetDefault(config, INCLUDE_LEFT_ENCHANTMENT_FOR_COST, DEFAULT_INCLUDE_LEFT_ENCHANTMENT_FOR_COST);
nbSet += trySetDefault(config, DEBUG_LOGGING, DEFAULT_DEBUG_LOG);
nbSet += trySetDefault(config, VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG);
nbSet += trySetDefault(config, SHOW_CONSOLE_DEBUG_LOGGING, DEFAULT_SHOW_CONSOLE_DEBUG_LOGGING);
if (nbSet > 0) {
CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values.");
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);

View file

@ -71,21 +71,37 @@ open class CustomAnvil : JavaPlugin() {
var latestVer: String? = null
// Debug
val debugStorageQueue = ArrayDeque<String>()
private fun addToLogQueue(message: String) {
if(debugStorageQueue.size >= 200) {
// Let not store infinite debug logs
debugStorageQueue.removeFirst()
}
debugStorageQueue.addLast(message)
}
/**
* Logging handler
*/
@JvmStatic fun log(message: String) {
if (ConfigOptions.debugLog) {
instance.logger.info(message)
if(ConfigOptions.showDebugLogInConsole)
instance.logger.info(message)
addToLogQueue(message)
}
}
/**
* Vebose Logging handler
*/
fun verboseLog(message: String) {
@JvmStatic fun verboseLog(message: String) {
if (ConfigOptions.verboseDebugLog) {
instance.logger.info(message)
if(ConfigOptions.showDebugLogInConsole)
instance.logger.info(message)
addToLogQueue(message)
}
}

View file

@ -76,6 +76,8 @@ object ConfigOptions {
const val IMMUTABLE_ENCHANTMENT_LIST = "immutable_enchantments"
const val INCLUDE_LEFT_ENCHANTMENT_FOR_COST = "include_left_enchantment_for_cost"
// Monetary configs
const val MONETARY_USAGE_ROOT = "monetary_cost"
const val SHOULD_USE_MONEY = "$MONETARY_USAGE_ROOT.enabled"
@ -89,6 +91,7 @@ object ConfigOptions {
// Debug flag
const val DEBUG_LOGGING = "debug_log"
const val VERBOSE_DEBUG_LOGGING = "debug_log_verbose"
const val SHOW_CONSOLE_DEBUG_LOGGING = "display_debug_in_console"
// ----------------------
// Default config values
@ -110,6 +113,8 @@ object ConfigOptions {
const val DEFAULT_ENCHANT_COUNT_LIMIT = -1
const val DEFAULT_INCLUDE_LEFT_ENCHANTMENT_FOR_COST = false
// Color related config
const val DEFAULT_ALLOW_COLOR_CODE = false
const val DEFAULT_ALLOW_HEXADECIMAL_COLOR = false
@ -125,8 +130,12 @@ object ConfigOptions {
const val DEFAULT_MONEY_MULTIPLIER = 1.0
// Debug flag
private const val DEFAULT_DEBUG_LOG = false
private const val DEFAULT_VERBOSE_DEBUG_LOG = false
const val DEFAULT_DEBUG_LOG = false
const val DEFAULT_VERBOSE_DEBUG_LOG = false
const val DEFAULT_SHOW_CONSOLE_DEBUG_LOGGING = false
var OVERRIDE_DEBUG_LOG: Boolean? = null
var OVERRIDE_VERBOSE_DEBUG_LOG: Boolean? = null
// Dialog menu rename
const val DEFAULT_DIALOG_RENAME_ENABLED = false
@ -431,11 +440,21 @@ object ConfigOptions {
?: DEFAULT_ENCHANT_COUNT_LIMIT
}
val includeLeftEnchantmentForCost: Boolean
get() {
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(INCLUDE_LEFT_ENCHANTMENT_FOR_COST, DEFAULT_INCLUDE_LEFT_ENCHANTMENT_FOR_COST)
}
/**
* Whether to show debug logging
*/
val debugLog: Boolean
get() {
val overrider = OVERRIDE_DEBUG_LOG
if(overrider != null) return overrider
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(DEBUG_LOGGING, DEFAULT_DEBUG_LOG)
@ -446,11 +465,27 @@ object ConfigOptions {
*/
val verboseDebugLog: Boolean
get() {
val overrider = OVERRIDE_VERBOSE_DEBUG_LOG
if(overrider != null) return overrider
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(VERBOSE_DEBUG_LOGGING, DEFAULT_VERBOSE_DEBUG_LOG)
}
/**
* Whether to show debug in console
*/
val showDebugLogInConsole: Boolean
get() {
val overrider = OVERRIDE_VERBOSE_DEBUG_LOG
if(overrider != null) return overrider
return ConfigHolder.DEFAULT_CONFIG
.config
.getBoolean(SHOW_CONSOLE_DEBUG_LOGGING, DEFAULT_SHOW_CONSOLE_DEBUG_LOGGING)
}
/**
* Is the dialog menu for rename enabled
*/

View file

@ -19,23 +19,6 @@ object ItemUtil {
*/
fun ItemStack.isEnchantedBook() = type == ENCHANTED_BOOK
/**
* Find the enchantment map for this [ItemStack] and return it as a [MutableMap]
*/
fun ItemStack.findEnchantments(): MutableMap<CAEnchantment, Int> = CAEnchantment.getEnchants(this)
/**
* Apply an [enchantments] map to this [ItemStack]
*/
fun ItemStack.setEnchantmentsUnsafe(enchantments: Map<CAEnchantment, Int>) {
CAEnchantment.clearEnchants(this)
enchantments.forEach { (enchantment, level) ->
enchantment.addEnchantmentUnsafe(this, level)
}
}
/**
* Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values

View file

@ -3,10 +3,8 @@ package xyz.alexcrea.cuanvil.anvil
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook
import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.ChatColor
import org.bukkit.Material
@ -16,6 +14,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.view.AnvilView
import org.bukkit.persistence.PersistentDataType
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
@ -187,16 +186,21 @@ object AnvilMergeLogic {
player: Player,
first: ItemStack, second: ItemStack
): AnvilResult {
val newEnchants = first.findEnchantments()
.combineWith(second.findEnchantments(), first, player)
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
val firstEnchants = EnchantmentApi.getEnchantments(first)
val secondEnchants = EnchantmentApi.getEnchantments(second)
// newEnchants will be mutated by combineWith
val newEnchants = HashMap(firstEnchants)
newEnchants.combineWith(secondEnchants, first, player)
var hasChanged = !isIdentical(firstEnchants, newEnchants)
val resultItem = DependencyManager.cloneItem(player, first)
val cost = AnvilCost()
if (hasChanged) {
resultItem.setEnchantmentsUnsafe(newEnchants)
EnchantmentApi.setEnchantments(resultItem, newEnchants)
// Calculate enchantment cost
AnvilXpUtil.getRightValues(second, resultItem, cost)
AnvilXpUtil.getRightValues(first, second, resultItem, cost)
}
// Calculate repair cost

View file

@ -21,6 +21,7 @@ class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
"gui", editConfigCommand,
"reload", ReloadExecutor(),
"diagnostic", DiagnosticExecutor(),
"debug", DebugToggleExecutor(),
"help", helpCommand,
)

View file

@ -0,0 +1,119 @@
package xyz.alexcrea.cuanvil.command
import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions
import net.md_5.bungee.api.chat.ClickEvent
import net.md_5.bungee.api.chat.HoverEvent
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.api.chat.hover.content.Text
import org.bukkit.ChatColor
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import xyz.alexcrea.cuanvil.command.DiagnosticExecutor.Companion.NO_DIAG_PERM
class DebugToggleExecutor : CASubCommand() {
override fun description(): String {
return "Used to toggle debug logs and retrieve it"
}
override fun allowed(sender: CommandSender): Boolean {
return sender.hasPermission(CustomAnvil.diagnosticPermission)
}
override fun executeCommand(
sender: CommandSender,
cmd: Command,
cmdstr: String,
args: Array<out String>
): Boolean {
if (!allowed(sender)) {
sender.sendMessage(NO_DIAG_PERM)
return false
}
if (args.isEmpty()) {
sender.sendMessage("Need to specify a subcommand: \"toggle\" or \"get\"")
return true
}
when (args[0].lowercase()) {
"toggle" -> executeToggle(sender, args)
"get" -> executeGet(sender)
"get-and-clear" -> {
executeGet(sender)
CustomAnvil.debugStorageQueue.clear()
}
"clear" -> {
CustomAnvil.debugStorageQueue.clear()
sender.sendMessage("Log Cleared")
}
else -> return false
}
return true
}
private fun executeToggle(sender: CommandSender, args: Array<out String>) {
if (args.size < 2) {
sender.sendMessage("Need to specify which type of debug to toggle: \"default\" or \"verbose\"")
return
}
when (args[1].lowercase()) {
"default" -> {
ConfigOptions.OVERRIDE_DEBUG_LOG = !ConfigOptions.debugLog
sender.sendMessage("Debug toggle to: ${ConfigOptions.debugLog}")
}
"verbose" -> {
ConfigOptions.OVERRIDE_VERBOSE_DEBUG_LOG = !ConfigOptions.verboseDebugLog
sender.sendMessage("Debug toggle to: ${ConfigOptions.verboseDebugLog}")
}
else -> sender.sendMessage("Invalid debug type: ${args[1]}")
}
}
private fun executeGet(sender: CommandSender) {
val stb = StringBuilder("Debug Log data:")
if (CustomAnvil.debugStorageQueue.isEmpty()) {
sender.sendMessage("No log to show ? make sure you tried with debug log toggled (/ca debug toggle)")
return
}
stb.append("\nFound ${CustomAnvil.debugStorageQueue.size} lines\n")
for (log in CustomAnvil.debugStorageQueue) {
stb.append('\n').append(log)
}
if (sender is Player) {
val message = TextComponent(ChatColor.GREEN.toString() + "Click to copy log data")
message.clickEvent = ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, stb.toString())
message.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, Text("§7Click to copy"))
sender.spigot().sendMessage(message);
} else {
sender.sendMessage(stb.toString())
}
}
override fun tabCompleter(sender: CommandSender, args: Array<out String>, list: MutableList<String>) {
if (!allowed(sender)) return
list.addAll(
when (args.size) {
1 -> listOf("toggle", "get", "get-and-clear", "clear")
2 -> when (args[0].lowercase()) {
"toggle" -> listOf("default", "verbose")
else -> listOf()
}
else -> listOf()
}
)
}
}

View file

@ -34,7 +34,7 @@ import java.util.stream.Collectors
class DiagnosticExecutor : CASubCommand() {
companion object {
private const val NO_DIAG_PERM = "You do not have permission to diagnostic this server"
const val NO_DIAG_PERM = "You do not have permission to diagnostic this server"
fun fetchNMSType(): String {
val packetManager = DependencyManager.packetManager

View file

@ -5,6 +5,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
@ -262,7 +263,8 @@ class EnchantConflictManager {
}
if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) {
val partialItem = createPartialResult(item, immutableEnchants)
val partialItem = item.clone()
EnchantmentApi.setEnchantments(partialItem, immutableEnchants)
if (newEnchant.isItemConflict(immutableEnchants, type, partialItem)) {
return ConflictType.ITEM_CONFLICT
@ -273,17 +275,6 @@ class EnchantConflictManager {
return result
}
private fun createPartialResult(item: ItemStack, enchantments: Map<CAEnchantment, Int>): ItemStack {
val newItem = item.clone()
CAEnchantment.clearEnchants(newItem)
enchantments.forEach { enchantment ->
enchantment.key.addEnchantmentUnsafe(newItem, enchantment.value)
}
return newItem
}
}
/**

View file

@ -106,12 +106,17 @@ class AnvilResultListener : Listener {
if (canMerge) {
val result = AnvilMergeLogic.doMerge(view, player, leftItem, rightItem)
extractAnvilResult(
val worked = extractAnvilResult(
event, player, view,
null, 0,
null, 0,
result
)
if(!worked) {
CustomAnvil.verboseLog("Merge extract failed. reset the displayed price")
// Reset the price
AnvilXpUtil.setAnvilResult(inventory, view, player, result)
}
return
}
@ -271,11 +276,20 @@ class AnvilResultListener : Listener {
val cost = result.cost
if (cost.isMonetary) {
val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost())
if (!result) return false
if (!result) {
CustomAnvil.verboseLog("Could not remove monetary cost ${cost.asMonetaryCost()}")
return false
}
} else {
val xpCost = cost.filteredXpCost()
if (xpCost > AnvilXpUtil.maximumXpCost(result.ignoreXpRules)) return false
if (player.level < xpCost) return false
if (xpCost > AnvilXpUtil.maximumXpCost(result.ignoreXpRules)) {
CustomAnvil.verboseLog("Cost above maximum $xpCost > ${AnvilXpUtil.maximumXpCost(result.ignoreXpRules)}")
return false
}
if (player.level < xpCost) {
CustomAnvil.verboseLog("Player do not have enough xp ${player.level} < $xpCost")
return false
}
player.level -= xpCost
}
@ -293,7 +307,10 @@ class AnvilResultListener : Listener {
rightRemoveCount: Int,
result: AnvilResult
): Boolean {
if (result.isEmpty()) return false
if (result.isEmpty()) {
CustomAnvil.verboseLog("Merge result is empty")
return false
}
// To avoid vanilla, we cancel the event
event.result = Event.Result.DENY
@ -301,7 +318,10 @@ class AnvilResultListener : Listener {
val cost = result.cost
processCost(view, player, cost)
if (!cost.valid && player.gameMode != GameMode.CREATIVE) return false
if (!cost.valid && player.gameMode != GameMode.CREATIVE) {
CustomAnvil.verboseLog("Player cannot afford the cost")
return false
}
// Where should we get the item
val slotDestination = getActionSlot(event, player)

View file

@ -168,11 +168,7 @@ class PrepareAnvilListener : Listener {
private fun applyResult(event: PrepareAnvilEvent, player: Player, result: AnvilResult) {
event.result = result.item
if (result.item == null) {
AnvilXpUtil.onNoResult(player, event.view)
return
}
AnvilXpUtil.setAnvilInvCost(event.view, player, result.cost, result.ignoreXpRules)
AnvilXpUtil.setAnvilResult(event.view, player, result)
}
}

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.util.anvil
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.NamespacedKey
@ -14,7 +13,9 @@ import org.bukkit.inventory.meta.Repairable
import org.bukkit.inventory.view.AnvilView
import org.bukkit.persistence.PersistentDataType
import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
@ -28,7 +29,23 @@ object AnvilXpUtil {
const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost"
/**
* Display the required cost (either as xp or as )
* Display the required cost (either as xp or as money) or reset anvil price depending on result
*/
fun setAnvilResult(
inventory: AnvilInventory,
view: InventoryView,
player: Player,
result: AnvilResult) {
if(result.item == null) {
onNoResult(player, view)
return
}
setAnvilInvCost(inventory, view, player, result.cost, result.ignoreXpRules)
}
/**
* Display the required cost (either as xp or as money)
*/
fun setAnvilInvCost(
view: AnvilView,
@ -216,17 +233,21 @@ object AnvilXpUtil {
}
/**
* Function to calculate right enchantment values
* Function to calculate enchantments values
* it include enchantment placed on final item and conflicting enchantment
*/
fun getRightValues(right: ItemStack, result: ItemStack, cost: AnvilCost) {
fun getRightValues(left: ItemStack, right: ItemStack, result: ItemStack, cost: AnvilCost) {
// Calculate right value and illegal enchant penalty
val rightIsFormBook = right.isEnchantedBook()
val resultEnchs = result.findEnchantments()
val rightEnchs = EnchantmentApi.getEnchantments(right)
val resultEnchs = EnchantmentApi.getEnchantments(result)
val resultEnchsKeys = HashMap(resultEnchs)
for (enchantment in right.findEnchantments()) {
var rightValue = 0
var leftValue = 0
for (enchantment in rightEnchs) {
// 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
@ -248,13 +269,31 @@ object AnvilXpUtil {
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)")
cost.enchantment += value
CustomAnvil.log("Value for sacrifice item ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
rightValue += value
}
if(ConfigOptions.includeLeftEnchantmentForCost) {
val leftIsFormBook = left.isEnchantedBook()
val leftEnchs = EnchantmentApi.getEnchantments(left)
for (enchantment in leftEnchs) {
// Do not process enchantment that are present on the sacrifice
if(rightEnchs.contains(enchantment.key)) continue
val resultLevel = resultEnchs.getOrDefault(enchantment.key, 0)
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, leftIsFormBook)
val value = resultLevel * enchantmentMultiplier
CustomAnvil.log("Value for left item ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
leftValue += value
}
}
cost.enchantment = rightValue + leftValue
CustomAnvil.log(
"Calculated right values: " +
"rightValue: ${cost.enchantment}, " +
"rightValue: ${rightValue}, " +
"leftValue: ${leftValue}, " +
"illegalPenalty: ${cost.illegalPenalty}"
)
}

View file

@ -119,6 +119,9 @@ permission_needed_for_dialog_rename: false
# For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true
# Also count left item enchantments for the final price
include_left_enchantment_for_cost: false
# Override limits for specific enchants
#
# Enchantments not listed here will use the value of default_limit
@ -171,8 +174,8 @@ enchant_limits:
# Multipliers used to calculate the enchantment's value in repair/combining
#
# Values here are pulled from the fandom wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments
# Values here are pulled from the minecraft wiki:
# https://minecraft.wiki/w/Anvil_mechanics#Costs_for_combining_enchantments
#
# If an enchantment is missing values here, or is less than 0, it will default to 0
#
@ -466,4 +469,7 @@ debug_log: false
# Whether to show verbose debug logging
debug_log_verbose: false
# Whether to show inside the console or require to run /ca debug get
display_debug_in_console: false
configVersion: 1.11.0

View file

@ -13,6 +13,7 @@ import org.bukkit.inventory.meta.Repairable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.Assertions;
import xyz.alexcrea.cuanvil.api.EnchantmentApi;
import xyz.alexcrea.cuanvil.data.AnvilClickTestData;
import xyz.alexcrea.cuanvil.data.AnvilFuseTestData;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
@ -47,7 +48,7 @@ public class AnvilFuseTestUtil {
}
ItemStack item = new ItemStack(material);
ItemUtil.INSTANCE.setEnchantmentsUnsafe(item, enchantmentMap);
EnchantmentApi.setEnchantments(item, enchantmentMap);
ItemMeta meta = item.getItemMeta();
((Repairable) meta).setRepairCost(repairCost);