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 # For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # 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 # Multipliers used to calculate the enchantment's value in repair/combining
# #
# Values here are pulled from the fandom wiki: # Values here are pulled from the minecraft wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments # 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 # 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 # Whether to show verbose debug logging
debug_log_verbose: false 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 configVersion: 1.15.5
lowMinecraftVersion: 1.21.11 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 # For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # 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 # Multipliers used to calculate the enchantment's value in repair/combining
# #
# Values here are pulled from the fandom wiki: # Values here are pulled from the minecraft wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments # 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 # 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 # Whether to show verbose debug logging
debug_log_verbose: false 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 configVersion: 1.11.0
lowMinecraftVersion: 1.21.9 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 # For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # 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 # Multipliers used to calculate the enchantment's value in repair/combining
# #
# Values here are pulled from the fandom wiki: # Values here are pulled from the minecraft wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments # 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 # 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 # Whether to show verbose debug logging
debug_log_verbose: false 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 configVersion: 1.11.0

View file

@ -5,6 +5,7 @@ import io.delilaheve.util.ConfigOptions;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import xyz.alexcrea.cuanvil.config.ConfigHolder; import xyz.alexcrea.cuanvil.config.ConfigHolder;
@ -143,6 +144,7 @@ public class EnchantmentApi {
/** /**
* Get every registered custom anvil enchantments. * Get every registered custom anvil enchantments.
*
* @return An immutable map of enchantment key as map key and custom anvil enchantment as value. * @return An immutable map of enchantment key as map key and custom anvil enchantment as value.
*/ */
@NotNull @NotNull
@ -152,6 +154,7 @@ public class EnchantmentApi {
/** /**
* Write the default level and rarity configuration of the enchantment. * Write the default level and rarity configuration of the enchantment.
*
* @param enchantment The enchantment to write default configuration * @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. * @return Return false if override is false and a configuration exist. true otherwise.
@ -204,7 +207,10 @@ public class EnchantmentApi {
} }
/** /**
* 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 * @param operation An optimised get enchantments operation
*/ */
public static void addBulkGet(@NotNull BulkGetEnchantOperation operation) { public static void addBulkGet(@NotNull BulkGetEnchantOperation operation) {
@ -213,10 +219,60 @@ public class EnchantmentApi {
/** /**
* Add a bulk clean operator. * 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); 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 @Override
public void bulkClear(@NotNull ItemStack item, @NotNull ItemMeta meta) { 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()); 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_RENAME_USE_PERMISSION, DEFAULT_DIALOG_RENAME_USE_PERMISSION);
nbSet += trySetDefault(config, DIALOG_KEEP_USER_TEXT, DEFAULT_DIALOG_KEEP_USER_TEXT); 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) { if (nbSet > 0) {
CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values."); CustomAnvil.instance.getLogger().info("Adding " + nbSet + " absent default config values.");
ConfigHolder.DEFAULT_CONFIG.saveToDisk(true); ConfigHolder.DEFAULT_CONFIG.saveToDisk(true);

View file

@ -71,21 +71,37 @@ open class CustomAnvil : JavaPlugin() {
var latestVer: String? = null 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 * Logging handler
*/ */
@JvmStatic fun log(message: String) { @JvmStatic fun log(message: String) {
if (ConfigOptions.debugLog) { if (ConfigOptions.debugLog) {
if(ConfigOptions.showDebugLogInConsole)
instance.logger.info(message) instance.logger.info(message)
addToLogQueue(message)
} }
} }
/** /**
* Vebose Logging handler * Vebose Logging handler
*/ */
fun verboseLog(message: String) { @JvmStatic fun verboseLog(message: String) {
if (ConfigOptions.verboseDebugLog) { if (ConfigOptions.verboseDebugLog) {
if(ConfigOptions.showDebugLogInConsole)
instance.logger.info(message) instance.logger.info(message)
addToLogQueue(message)
} }
} }

View file

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

View file

@ -19,23 +19,6 @@ object ItemUtil {
*/ */
fun ItemStack.isEnchantedBook() = type == ENCHANTED_BOOK 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 * Set this [ItemStack]s durability from a combination of the
* [first] and [second] item's durability values * [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.CustomAnvil
import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.combineWith import io.delilaheve.util.EnchantmentUtil.combineWith
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook import io.delilaheve.util.ItemUtil.isEnchantedBook
import io.delilaheve.util.ItemUtil.repairFrom import io.delilaheve.util.ItemUtil.repairFrom
import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe
import io.delilaheve.util.ItemUtil.unitRepair import io.delilaheve.util.ItemUtil.unitRepair
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Material import org.bukkit.Material
@ -16,6 +14,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.inventory.view.AnvilView import org.bukkit.inventory.view.AnvilView
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog import xyz.alexcrea.cuanvil.dialog.AnvilRenameDialog
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
@ -187,16 +186,21 @@ object AnvilMergeLogic {
player: Player, player: Player,
first: ItemStack, second: ItemStack first: ItemStack, second: ItemStack
): AnvilResult { ): AnvilResult {
val newEnchants = first.findEnchantments() val firstEnchants = EnchantmentApi.getEnchantments(first)
.combineWith(second.findEnchantments(), first, player) val secondEnchants = EnchantmentApi.getEnchantments(second)
var hasChanged = !isIdentical(first.findEnchantments(), newEnchants)
// 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 resultItem = DependencyManager.cloneItem(player, first)
val cost = AnvilCost() val cost = AnvilCost()
if (hasChanged) { if (hasChanged) {
resultItem.setEnchantmentsUnsafe(newEnchants) EnchantmentApi.setEnchantments(resultItem, newEnchants)
// Calculate enchantment cost // Calculate enchantment cost
AnvilXpUtil.getRightValues(second, resultItem, cost) AnvilXpUtil.getRightValues(first, second, resultItem, cost)
} }
// Calculate repair cost // Calculate repair cost

View file

@ -21,6 +21,7 @@ class CustomAnvilCommand(plugin: CustomAnvil) : CommandExecutor, TabCompleter {
"gui", editConfigCommand, "gui", editConfigCommand,
"reload", ReloadExecutor(), "reload", ReloadExecutor(),
"diagnostic", DiagnosticExecutor(), "diagnostic", DiagnosticExecutor(),
"debug", DebugToggleExecutor(),
"help", helpCommand, "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() { class DiagnosticExecutor : CASubCommand() {
companion object { 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 { fun fetchNMSType(): String {
val packetManager = DependencyManager.packetManager val packetManager = DependencyManager.packetManager

View file

@ -5,6 +5,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
import org.bukkit.enchantments.Enchantment import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment import xyz.alexcrea.cuanvil.enchant.AdditionalTestEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantment import xyz.alexcrea.cuanvil.enchant.CAEnchantment
import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry
@ -262,7 +263,8 @@ class EnchantConflictManager {
} }
if ((result != ConflictType.ITEM_CONFLICT) && (newEnchant is AdditionalTestEnchantment)) { 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)) { if (newEnchant.isItemConflict(immutableEnchants, type, partialItem)) {
return ConflictType.ITEM_CONFLICT return ConflictType.ITEM_CONFLICT
@ -273,17 +275,6 @@ class EnchantConflictManager {
return result 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) { if (canMerge) {
val result = AnvilMergeLogic.doMerge(view, player, leftItem, rightItem) val result = AnvilMergeLogic.doMerge(view, player, leftItem, rightItem)
extractAnvilResult( val worked = extractAnvilResult(
event, player, view, event, player, view,
null, 0, null, 0,
null, 0, null, 0,
result result
) )
if(!worked) {
CustomAnvil.verboseLog("Merge extract failed. reset the displayed price")
// Reset the price
AnvilXpUtil.setAnvilResult(inventory, view, player, result)
}
return return
} }
@ -271,11 +276,20 @@ class AnvilResultListener : Listener {
val cost = result.cost val cost = result.cost
if (cost.isMonetary) { if (cost.isMonetary) {
val result = EconomyManager.economy!!.remove(player, cost.asMonetaryCost()) 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 { } else {
val xpCost = cost.filteredXpCost() val xpCost = cost.filteredXpCost()
if (xpCost > AnvilXpUtil.maximumXpCost(result.ignoreXpRules)) return false if (xpCost > AnvilXpUtil.maximumXpCost(result.ignoreXpRules)) {
if (player.level < xpCost) return false 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 player.level -= xpCost
} }
@ -293,7 +307,10 @@ class AnvilResultListener : Listener {
rightRemoveCount: Int, rightRemoveCount: Int,
result: AnvilResult result: AnvilResult
): Boolean { ): Boolean {
if (result.isEmpty()) return false if (result.isEmpty()) {
CustomAnvil.verboseLog("Merge result is empty")
return false
}
// To avoid vanilla, we cancel the event // To avoid vanilla, we cancel the event
event.result = Event.Result.DENY event.result = Event.Result.DENY
@ -301,7 +318,10 @@ class AnvilResultListener : Listener {
val cost = result.cost val cost = result.cost
processCost(view, player, 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 // Where should we get the item
val slotDestination = getActionSlot(event, player) val slotDestination = getActionSlot(event, player)

View file

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

View file

@ -3,7 +3,6 @@ package xyz.alexcrea.cuanvil.util.anvil
import io.delilaheve.CustomAnvil import io.delilaheve.CustomAnvil
import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ConfigOptions
import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.EnchantmentUtil.enchantmentName
import io.delilaheve.util.ItemUtil.findEnchantments
import io.delilaheve.util.ItemUtil.isEnchantedBook import io.delilaheve.util.ItemUtil.isEnchantedBook
import org.bukkit.GameMode import org.bukkit.GameMode
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
@ -14,7 +13,9 @@ import org.bukkit.inventory.meta.Repairable
import org.bukkit.inventory.view.AnvilView import org.bukkit.inventory.view.AnvilView
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
import xyz.alexcrea.cuanvil.anvil.AnvilCost import xyz.alexcrea.cuanvil.anvil.AnvilCost
import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult
import xyz.alexcrea.cuanvil.anvil.AnvilUseType import xyz.alexcrea.cuanvil.anvil.AnvilUseType
import xyz.alexcrea.cuanvil.api.EnchantmentApi
import xyz.alexcrea.cuanvil.config.ConfigHolder import xyz.alexcrea.cuanvil.config.ConfigHolder
import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.DependencyManager
import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager
@ -28,7 +29,23 @@ object AnvilXpUtil {
const val EXCLUSIVE_PENALTY_PREFIX = "repair_cost" 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( fun setAnvilInvCost(
view: AnvilView, 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 * 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 // Calculate right value and illegal enchant penalty
val rightIsFormBook = right.isEnchantedBook() val rightIsFormBook = right.isEnchantedBook()
val resultEnchs = result.findEnchantments() val rightEnchs = EnchantmentApi.getEnchantments(right)
val resultEnchs = EnchantmentApi.getEnchantments(result)
val resultEnchsKeys = HashMap(resultEnchs) 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 // count enchant as illegal enchant if it conflicts with another enchant or not in result
if ((enchantment.key !in resultEnchsKeys)) { if ((enchantment.key !in resultEnchsKeys)) {
resultEnchsKeys[enchantment.key] = enchantment.value resultEnchsKeys[enchantment.key] = enchantment.value
@ -248,13 +269,31 @@ object AnvilXpUtil {
val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook) val enchantmentMultiplier = ConfigOptions.enchantmentValue(enchantment.key, rightIsFormBook)
val value = resultLevel * enchantmentMultiplier val value = resultLevel * enchantmentMultiplier
CustomAnvil.log("Value for ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)") CustomAnvil.log("Value for sacrifice item ${enchantment.key.enchantmentName} level ${enchantment.value} is $value ($resultLevel * $enchantmentMultiplier)")
cost.enchantment += value 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( CustomAnvil.log(
"Calculated right values: " + "Calculated right values: " +
"rightValue: ${cost.enchantment}, " + "rightValue: ${rightValue}, " +
"leftValue: ${leftValue}, " +
"illegalPenalty: ${cost.illegalPenalty}" "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 # For practical reason. this only work when dialog rename is enabled
dialog_rename_keep_user_text: true 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 # Override limits for specific enchants
# #
# Enchantments not listed here will use the value of default_limit # 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 # Multipliers used to calculate the enchantment's value in repair/combining
# #
# Values here are pulled from the fandom wiki: # Values here are pulled from the minecraft wiki:
# https://minecraft.fandom.com/wiki/Anvil_mechanics#Costs_for_combining_enchantments # 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 # 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 # Whether to show verbose debug logging
debug_log_verbose: false 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 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import xyz.alexcrea.cuanvil.api.EnchantmentApi;
import xyz.alexcrea.cuanvil.data.AnvilClickTestData; import xyz.alexcrea.cuanvil.data.AnvilClickTestData;
import xyz.alexcrea.cuanvil.data.AnvilFuseTestData; import xyz.alexcrea.cuanvil.data.AnvilFuseTestData;
import xyz.alexcrea.cuanvil.enchant.CAEnchantment; import xyz.alexcrea.cuanvil.enchant.CAEnchantment;
@ -47,7 +48,7 @@ public class AnvilFuseTestUtil {
} }
ItemStack item = new ItemStack(material); ItemStack item = new ItemStack(material);
ItemUtil.INSTANCE.setEnchantmentsUnsafe(item, enchantmentMap); EnchantmentApi.setEnchantments(item, enchantmentMap);
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
((Repairable) meta).setRepairCost(repairCost); ((Repairable) meta).setRepairCost(repairCost);