diff --git a/README.md b/README.md index 829eeb0..cc868c5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ the plugin can be downloaded on - Display XP cost instead of "too expensive" when above level 40. (see below for more information) - Can handle some custom enchantment plugins (see below for more information) - Gui to configure the plugin in game. -- Support of color code and hexadecimal color +- Support use of color code, hexadecimal color and minimessage for color/decoration - (Experimental) Folia support (gui do not work) --- ### Permissions: @@ -41,9 +41,10 @@ ca.config.edit: Allow administrator to edit the plugin's config in game # Bellow permissions also require some config change to allow usage of features # usage of these permission is toggleable in basic config gui or config.yml -# Permissions related to use of color -ca.color.code: Allow player to use color code if enabled (toggleable) -ca.color.hex: Allow player to use hexadecimal color if enabled (toggleable) +# Permissions related to use of color and minimessage +ca.color.code: Allow player to use color code on rename if enabled (toggleable) +ca.color.hex: Allow player to use hexadecimal color on rename if enabled (toggleable) +ca.rename.minimessage: Allow player to use minimessage formating on rename if enabled (toggleable) (only legacy compatible at the time) # Permissions related to edition of the lore ca.lore_edit.book: Allow player to edit lore via book and quil if enabled (toggleable) diff --git a/build.gradle.kts b/build.gradle.kts index 64178a0..c961dcd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,9 @@ dependencies { // Paper paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") + // minimessage + implementation("net.kyori:adventure-text-minimessage:4.25.0") + // Gui library val inventoryFramework = "xyz.alexcrea.cuanvil.inventoryframework:IF-CustomAnvil:0.10.18.2" implementation(inventoryFramework) diff --git a/defaultconfigs/1.18/config.yml b/defaultconfigs/1.18/config.yml index fbe43f7..877cee9 100644 --- a/defaultconfigs/1.18/config.yml +++ b/defaultconfigs/1.18/config.yml @@ -59,8 +59,10 @@ sacrifice_illegal_enchant_cost: 1 # # Color code are prefixed by "&" and hexadecimal color by "#". # Color code will not be applied if it colors nothing. "&&" can be used to write "&". +# For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ allow_color_code: false allow_hexadecimal_color: false +allow_minimessage: false # Toggle if color should only be applicable if the player a certain permission. # @@ -301,9 +303,12 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true - use_cost: 0 + allow_hexadecimal_color: false + allow_minimessage: true remove: # If removing lore using book & quil is enabled @@ -318,10 +323,19 @@ lore_edit: shared_additive: false # If removing the lore consume the book & quil do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this book should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true paper: # Permission is ca.lore_edit.paper @@ -344,10 +358,13 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true + allow_hexadecimal_color: false + allow_minimessage: true color_use_cost: 0 - use_cost: 0 remove_line: # If removing lore line using paper is enabled @@ -360,10 +377,19 @@ lore_edit: shared_additive: false # If removing the lore line consume the paper do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this paper should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index f6817ba..cf9460e 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -59,8 +59,10 @@ sacrifice_illegal_enchant_cost: 1 # # Color code are prefixed by "&" and hexadecimal color by "#". # Color code will not be applied if it colors nothing. "&&" can be used to write "&". +# For minimessage search for minimessage formating https://docs.papermc.io/adventure/minimessage/format/ allow_color_code: false allow_hexadecimal_color: false +allow_minimessage: false # Toggle if color should only be applicable if the player a certain permission. # @@ -313,10 +315,13 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true - use_cost: 0 - + allow_hexadecimal_color: false + allow_minimessage: true + remove: # If removing lore using book & quil is enabled enabled: false @@ -330,17 +335,26 @@ lore_edit: shared_additive: false # If removing the lore consume the book & quil do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 - + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this book should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true + paper: # Permission is ca.lore_edit.paper use_permission: true # what order should the lines should get added/removed (start/end, if invalid or not present will be end) order: end - + append_line: # If adding lore line using paper is enabled enabled: false @@ -356,11 +370,14 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true + allow_hexadecimal_color: false + allow_minimessage: true color_use_cost: 0 - use_cost: 0 - + remove_line: # If removing lore line using paper is enabled enabled: false @@ -372,10 +389,19 @@ lore_edit: shared_additive: false # If removing the lore line consume the paper do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this paper should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index d6e76bf..d7807e9 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -59,8 +59,10 @@ sacrifice_illegal_enchant_cost: 1 # # Color code are prefixed by "&" and hexadecimal color by "#". # Color code will not be applied if it colors nothing. "&&" can be used to write "&". +# For minimessage search for minimessage formating https://docs.papermc.io/adventure/minimessage/format/ allow_color_code: false allow_hexadecimal_color: false +allow_minimessage: false # Toggle if color should only be applicable if the player a certain permission. # @@ -301,9 +303,12 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true - use_cost: 0 + allow_hexadecimal_color: false + allow_minimessage: true remove: # If removing lore using book & quil is enabled @@ -318,10 +323,19 @@ lore_edit: shared_additive: false # If removing the lore consume the book & quil do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this book should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true paper: # Permission is ca.lore_edit.paper @@ -344,10 +358,13 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true + allow_hexadecimal_color: false + allow_minimessage: true color_use_cost: 0 - use_cost: 0 remove_line: # If removing lore line using paper is enabled @@ -360,10 +377,19 @@ lore_edit: shared_additive: false # If removing the lore line consume the paper do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this paper should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true # Whether to show debug logging debug_log: false diff --git a/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/util/PaperSpigtUtil.kt b/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/util/PaperSpigtUtil.kt new file mode 100644 index 0000000..3f709c7 --- /dev/null +++ b/nms/nms-common/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/util/PaperSpigtUtil.kt @@ -0,0 +1,102 @@ +package xyz.alexcrea.cuanvil.dependency.util + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.ItemMeta + +// Mostly made for paper, spigot and folia support +@Suppress("DEPRECATION") +object PlatformUtil { + + private fun hasClass(className: String): Boolean { + try { + Class.forName(className) + return true + } catch (_: ClassNotFoundException) { + return false + } + } + + private fun hasMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>): Boolean { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return true + } catch (_: NoSuchMethodException) { + return false + } + } + + val isPaper = hasClass("com.destroystokyo.paper.PaperConfig") || + hasClass("io.papermc.paper.configuration.Configuration") + + val isFolia = hasClass("io.papermc.paper.threadedregions.RegionizedServer") + + private val legacy_mm = LegacyComponentSerializer.legacySection() + + // Lore + fun ItemMeta.componentLore(): MutableList { + val lore: List? + if(isPaper){ + lore = this.lore() + } else { + val legacyLores = this.lore ?: return ArrayList() + + lore = ArrayList(legacyLores.size) + for (legacyLore in legacyLores) { + lore.add(legacy_mm.deserialize(legacyLore)) + } + } + + return lore ?: ArrayList() + } + + fun ItemMeta.setComponentLore(lore: List) { + if(isPaper){ + this.lore(lore) + } else { + val legacyLore = ArrayList(lore.size) + for (component in lore) { + legacyLore.add(if(component == null) null + else legacy_mm.serialize(component)) + } + + this.lore = legacyLore + } + } + + // Display name + private val useCustomName = hasMethod(ItemStack::class.java, "customName") + + fun ItemMeta.componentDisplayName(): Component? { + if(useCustomName){ + if(!this.hasCustomName()) return null + return this.customName() + }else if(isPaper){ + if(!this.hasDisplayName()) return null + return this.displayName() + } else { + if(!this.hasDisplayName()) return null + + val legacy = this.displayName + return legacy_mm.deserialize(legacy) + } + } + + fun ItemMeta.setComponentDisplayName(component: Component?) { + if(useCustomName){ + this.customName(component) + }else if(isPaper){ + this.displayName(component) + } else { + if(component == null){ + this.setDisplayName(null) + return + } + + val legacy = legacy_mm.serialize(component) + this.setDisplayName(legacy) + } + } + +} diff --git a/nms/v1_21R6/build.gradle.kts b/nms/v1_21R6/build.gradle.kts index f124fdd..3665d74 100644 --- a/nms/v1_21R6/build.gradle.kts +++ b/nms/v1_21R6/build.gradle.kts @@ -11,7 +11,7 @@ dependencies { implementation(project(":nms:nms-common")) // Used for nms - paperweight.paperDevBundle("1.21.9-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") } repositories { diff --git a/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java b/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java index 363bd6a..f41842d 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java +++ b/src/main/java/xyz/alexcrea/cuanvil/update/PluginSetDefault.java @@ -48,7 +48,6 @@ public class PluginSetDefault { nbSet += trySetDefault(config, path + ALLOW_HEX_COLOR, DEFAULT_ALLOW_HEX_COLOR); nbSet += trySetDefault(config, path + USE_COLOR_COST, DEFAULT_USE_COLOR_COST); } else { - nbSet += trySetDefault(config, path + REMOVE_COLOR_ON_LORE_REMOVE, DEFAULT_REMOVE_COLOR_ON_LORE_REMOVE); nbSet += trySetDefault(config, path + REMOVE_COLOR_COST, DEFAULT_REMOVE_COLOR_COST); } } diff --git a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt index 4e3dcb1..afb009f 100644 --- a/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt +++ b/src/main/kotlin/io/delilaheve/util/ConfigOptions.kt @@ -38,6 +38,7 @@ object ConfigOptions { // Color related config const val ALLOW_COLOR_CODE = "allow_color_code" const val ALLOW_HEXADECIMAL_COLOR = "allow_hexadecimal_color" + const val ALLOW_MINIMESSAGE = "allow_minimessage" const val PERMISSION_NEEDED_FOR_COLOR = "permission_needed_for_color" const val USE_OF_COLOR_COST = "use_of_color_cost" @@ -94,6 +95,7 @@ object ConfigOptions { // Color related config const val DEFAULT_ALLOW_COLOR_CODE = false const val DEFAULT_ALLOW_HEXADECIMAL_COLOR = false + const val DEFAULT_ALLOW_MINIMESSAGE = false const val DEFAULT_PERMISSION_NEEDED_FOR_COLOR = true const val DEFAULT_USE_OF_COLOR_COST = 0 @@ -269,12 +271,22 @@ object ConfigOptions { .getBoolean(ALLOW_HEXADECIMAL_COLOR, DEFAULT_ALLOW_HEXADECIMAL_COLOR) } + /** + * Allow usage of minimessage formating + */ + val allowMinimessage: Boolean + get() { + return ConfigHolder.DEFAULT_CONFIG + .config + .getBoolean(ALLOW_MINIMESSAGE, DEFAULT_ALLOW_MINIMESSAGE) + } + /** * If one of the color component is enabled */ val renameColorPossible: Boolean get() { - return allowColorCode || allowHexadecimalColor + return allowColorCode || allowHexadecimalColor || allowMinimessage } /** diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/command/EditConfigExecutor.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/command/EditConfigExecutor.kt index 85761d9..f90f765 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/command/EditConfigExecutor.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/command/EditConfigExecutor.kt @@ -5,7 +5,7 @@ import org.bukkit.command.Command import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandSender import org.bukkit.entity.HumanEntity -import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil import xyz.alexcrea.cuanvil.gui.config.MainConfigGui import xyz.alexcrea.cuanvil.gui.util.GuiGlobalActions @@ -16,7 +16,7 @@ class EditConfigExecutor : CommandExecutor { sender.sendMessage(GuiGlobalActions.NO_EDIT_PERM) return false } - if(DependencyManager.isFolia){ + if(PlatformUtil.isFolia){ sender.sendMessage("§cIt look like you are using Folia. Sadly Custom Anvil do not support Config gui for Folia.") sender.sendMessage("§eIt is may come in a future version.") sender.sendMessage("") diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 46288e7..f399c9b 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -2,6 +2,7 @@ package xyz.alexcrea.cuanvil.dependency import com.willfp.eco.core.gui.player import io.delilaheve.CustomAnvil +import net.kyori.adventure.text.Component import org.bukkit.Bukkit import org.bukkit.ChatColor import org.bukkit.entity.HumanEntity @@ -22,6 +23,8 @@ import xyz.alexcrea.cuanvil.dependency.plugins.* import xyz.alexcrea.cuanvil.dependency.scheduler.BukkitScheduler import xyz.alexcrea.cuanvil.dependency.scheduler.FoliaScheduler import xyz.alexcrea.cuanvil.dependency.scheduler.TaskScheduler +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT import xyz.alexcrea.cuanvil.util.AnvilUseType import java.util.logging.Level @@ -29,8 +32,6 @@ import java.util.logging.Level @Suppress("unstableApiUsage") object DependencyManager { - var isFolia: Boolean = false - var isMockbukkit: Boolean = false lateinit var scheduler: TaskScheduler lateinit var packetManager: PacketManagerBase @@ -49,16 +50,11 @@ object DependencyManager { val pluginManager = Bukkit.getPluginManager() // Bukkit or Paper scheduler ? - isFolia = testIsFolia() - isMockbukkit = testIsMockbukkit() - - if (isFolia) { + scheduler = if (PlatformUtil.isFolia) { CustomAnvil.instance.logger.info("Folia detected... Custom Anvil Folia support is experimental. issues are more likely to happens.") - } - scheduler = - if (isMockbukkit) BukkitScheduler() - else FoliaScheduler() + FoliaScheduler() + } else BukkitScheduler() // Packet Manager val forceProtocolib = ConfigHolder.DEFAULT_CONFIG.config.getBoolean("force_protocolib", false) @@ -313,15 +309,15 @@ object DependencyManager { return bypass } - fun stripLore(item: ItemStack): ArrayList { - val lore = ArrayList() + fun stripLore(item: ItemStack): MutableList { val dummy = item.clone() enchantmentSquaredCompatibility?.stripLore(dummy) - val itemLore = dummy.itemMeta!!.lore - if (itemLore != null) lore.addAll(itemLore) + val itemLore = dummy.itemMeta?.componentLore() ?: return ArrayList() + val lore = ArrayList() + lore.addAll(itemLore) return lore } @@ -329,13 +325,4 @@ object DependencyManager { enchantmentSquaredCompatibility?.updateLore(item) } - private fun testIsFolia(): Boolean { - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer") - return true - } catch (e: ClassNotFoundException) { - return false - } - } - } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 428ee4e..8fd1cf4 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -16,6 +16,7 @@ import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import org.bukkit.inventory.view.AnvilView import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_LEFT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_INPUT_RIGHT import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener.Companion.ANVIL_OUTPUT_SLOT @@ -24,11 +25,13 @@ import xyz.alexcrea.cuanvil.util.AnvilLoreEditUtil import xyz.alexcrea.cuanvil.util.AnvilUseType import xyz.alexcrea.cuanvil.util.AnvilXpUtil import xyz.alexcrea.cuanvil.util.CustomRecipeUtil +import xyz.alexcrea.cuanvil.util.MiniMessageUtil import xyz.alexcrea.cuanvil.util.UnitRepairUtil.getRepair import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType import java.util.* import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicReference import kotlin.math.min @Suppress("unstableApiUsage") @@ -394,8 +397,6 @@ class AnvilResultListener : Listener { if (output != AnvilLoreEditUtil.handleLoreRemoveByBook(player, leftItem, xpCost)) return false // fill book meta - val meta = leftItem.itemMeta - if (meta == null || !meta.hasLore()) return false val lore = DependencyManager.stripLore(leftItem) if (lore.isEmpty()) return false @@ -409,7 +410,9 @@ class AnvilResultListener : Listener { val bookPage = StringBuilder() lore.forEach { if (bookPage.isNotEmpty()) bookPage.append('\n') - bookPage.append(it) + if(it == null) return@forEach + + bookPage.append(MiniMessageUtil.plain_text_mm.serialize(it)) } val resultPage = bookPage.toString() @@ -440,10 +443,10 @@ class AnvilResultListener : Listener { if (Material.PAPER != rightItem.type) return false val paperMeta = rightItem.itemMeta ?: return false - val editType = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false + val editTypeIsAppend = AnvilLoreEditUtil.paperLoreEditIsAppend(leftItem, rightItem) ?: return false val xpCost = AtomicInteger() - if (editType) { + if (editTypeIsAppend) { if (output != AnvilLoreEditUtil.handleLoreAppendByPaper(player, leftItem, rightItem, xpCost)) return false val paperCopy: ItemStack? @@ -453,7 +456,7 @@ class AnvilResultListener : Listener { // Remove custom name to paper paperCopy = rightItem.clone() paperCopy.amount = 1 - paperMeta.setDisplayName(null) + paperMeta.setComponentDisplayName(null) paperCopy.itemMeta = paperMeta } @@ -486,20 +489,18 @@ class AnvilResultListener : Listener { rightClone = null } else { val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd - var line = if (removeEnd) lore[lore.size - 1] + val line = if (removeEnd) lore[lore.size - 1] else lore[0] - // Overkill but uncolor the line - val tempList = ArrayList(1) - tempList.add(line) - AnvilLoreEditUtil.uncolorLines(player, tempList, LoreEditType.REMOVE_PAPER) - line = tempList[0] + // uncolor the line + val ref = AtomicReference(line) + AnvilLoreEditUtil.uncolorLine(player, ref, LoreEditType.REMOVE_PAPER) rightClone = rightItem.clone() rightClone.amount = 1 val resultMeta = rightClone.itemMeta ?: return false - resultMeta.setDisplayName(line) + resultMeta.setComponentDisplayName(ref.get()) rightClone.itemMeta = resultMeta } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index 291e625..739f600 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -186,24 +186,23 @@ class PrepareAnvilListener : Listener { private fun handleRename(resultItem: ItemStack, view: AnvilView, player: HumanEntity): Int { // Can be null - var inventoryName = ChatColor.stripColor(view.renameText) + var renameText = ChatColor.stripColor(view.renameText) var sumCost = 0 var useColor = false - if (ConfigOptions.renameColorPossible && inventoryName != null) { - val resultString = StringBuilder(inventoryName) - - useColor = AnvilColorUtil.handleColor( - resultString, player, + if (ConfigOptions.renameColorPossible && renameText != null) { + val component = AnvilColorUtil.handleColor( + renameText, player, ConfigOptions.permissionNeededForColor, - ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, + ConfigOptions.allowColorCode, ConfigOptions.allowHexadecimalColor, ConfigOptions.allowMinimessage, AnvilColorUtil.ColorUseType.RENAME ) - if (useColor) { - inventoryName = resultString.toString() + if (component != null) { + renameText = MiniMessageUtil.legacy_mm.serialize(component) sumCost += ConfigOptions.useOfColorCost + useColor = true } } @@ -214,8 +213,8 @@ class PrepareAnvilListener : Listener { else if (useColor) it.displayName else ChatColor.stripColor(it.displayName) - if (!displayName.contentEquals(inventoryName)) { - it.setDisplayName(inventoryName) + if (!displayName.contentEquals(renameText)) { + it.setDisplayName(renameText) resultItem.itemMeta = it sumCost += ConfigOptions.itemRenameCost @@ -233,10 +232,10 @@ class PrepareAnvilListener : Listener { ) { val newEnchants = first.findEnchantments() .combineWith(second.findEnchantments(), first, player) - var hasChanged = !isIdentical(first.findEnchantments(), newEnchants); + var hasChanged = !isIdentical(first.findEnchantments(), newEnchants) val resultItem = first.clone() - var anvilCost = 0; + var anvilCost = 0 if(hasChanged){ resultItem.setEnchantmentsUnsafe(newEnchants) // Calculate enchantment cost @@ -248,7 +247,7 @@ class PrepareAnvilListener : Listener { // we only need to be concerned with repair when neither item is a book val repaired = resultItem.repairFrom(first, second) anvilCost += if (repaired) ConfigOptions.itemRepairCost else 0 - hasChanged = hasChanged || repaired; + hasChanged = hasChanged || repaired } // Test/stop if nothing changed. diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt index 277a8d1..35ed486 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilColorUtil.kt @@ -1,97 +1,174 @@ package xyz.alexcrea.cuanvil.util +import net.kyori.adventure.text.Component import org.bukkit.permissions.Permissible import java.util.regex.Matcher import java.util.regex.Pattern +import kotlin.text.indexOf object AnvilColorUtil { private val HEX_PATTERN: Pattern = Pattern.compile("#[A-Fa-f0-9]{6}") // pattern to find hexadecimal string private val TRANSFORMED_HEX_PATTERN = Pattern.compile("§x(§[0-9a-fA-F]){6}") // pattern to find minecraft hex string - /** - * Color a stringbuilder object depending on allowed color type and player permissions on color use type - * @return if the stringbuilder was changed and color applied - */ - fun handleColor( - textToColor: StringBuilder, + class ColorPermissions( + val canUseColorCode: Boolean, + val canUseHexColor: Boolean, + val canUseMinimessage: Boolean + ) { + fun allowed(): Boolean { + return canUseColorCode || canUseHexColor || canUseMinimessage + } + + fun onlyMinimessage(): Boolean { + return canUseMinimessage && !canUseColorCode && !canUseHexColor + } + } + + fun calculatePermissions( player: Permissible, usePermission: Boolean, allowColorCode: Boolean, allowHexadecimalColor: Boolean, + allowMinimessage: Boolean, useType: ColorUseType - ): Boolean { - if (!allowColorCode && !allowHexadecimalColor) return false + ): ColorPermissions { + if (!allowColorCode && !allowHexadecimalColor && !allowMinimessage) + return ColorPermissions( + canUseColorCode = false, + canUseHexColor = false, + canUseMinimessage = false + ) val canUseColorCode = allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission( useType.colorCodePerm )) + + val canUseMinimessage = + allowMinimessage && (!usePermission || useType.minimessagePerm == null || player.hasPermission( + useType.minimessagePerm + )) + val canUseHexColor = allowHexadecimalColor && (!usePermission || useType.hexColorPerm == null || player.hasPermission( useType.hexColorPerm )) - if ((!canUseColorCode) && (!canUseHexColor)) return false + return ColorPermissions(canUseColorCode, canUseHexColor, canUseMinimessage) + } + /** + * Color a string depending on allowed color type, color use type and player permissions + * @return colored component or null if nothing has been colored + */ + fun handleColor( + textToColorText: String, + player: Permissible, + usePermission: Boolean, + allowColorCode: Boolean, + allowHexadecimalColor: Boolean, + allowMinimessage: Boolean, + useType: ColorUseType + ): Component? { + val permission = calculatePermissions(player, usePermission, + allowColorCode, allowHexadecimalColor, allowMinimessage, + useType) + return handleColor(textToColorText, permission) + } + + /** + * Color a string depending on permitted use + * @return colored component or null if nothing has been colored + */ + fun handleColor( + textToColorText: String, + permission: ColorPermissions + ): Component? { + if(!permission.allowed()) return null + + val textToColor = StringBuilder(textToColorText) var useColor = false // Handle color code - if (canUseColorCode) { + if (permission.canUseColorCode) { // maybe should use LegacyComponentSerializer ? var nbReplacement = replaceAll(textToColor, "&", "§", 2) nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2) if (nbReplacement > 0) useColor = true } - if (canUseHexColor) { - val nbReplacement = replaceHexToColor(textToColor, 7) + if (permission.canUseHexColor) { + val nbReplacement = replaceHexToColor(textToColor, 7, permission.canUseMinimessage) if (nbReplacement > 0) useColor = true } - return useColor + val previousStr = textToColor.toString() + var result: Component = MiniMessageUtil.legacy_mm.deserialize(previousStr) + if(permission.canUseMinimessage) { + // we dance with formats here + val toMinimessage = MiniMessageUtil.mm.serialize(result) + val hackySolution = toMinimessage.replace("\\<", "<") + val fromMinimessage = MiniMessageUtil.mm.deserialize(hackySolution) + val asPlain = MiniMessageUtil.plain_text_mm.serialize(fromMinimessage) + + if(previousStr != asPlain){ + useColor = true + result = fromMinimessage + } + } + + return if(useColor) result + else null } /** - * Revert a stringbuilder to a state where applying handleColor with the same options would give the same result - * @return if the stringbuilder was changed and color unapplied + * Best effort to revert a component to the smallest allowed string + * that would result in it getting closest as possible to handleColor + * with current set of permitted use + * @return a new component if had any change. null otherwise */ - fun revertColor( - colorToText: StringBuilder, - player: Permissible, - usePermission: Boolean, - allowColorCode: Boolean, - allowHexadecimalColor: Boolean, - useType: ColorUseType - ): Boolean { - if (!allowColorCode && !allowHexadecimalColor) return false + fun revertColorSmallest( + component: Component?, + permission: ColorPermissions + ): String? { + if(!permission.allowed() || component == null) return null - val canUseColorCode = - allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission( - useType.colorCodePerm - )) - val canUseHexColor = - allowHexadecimalColor && (!usePermission || useType.hexColorPerm == null || player.hasPermission( - useType.hexColorPerm - )) + val transformed = MiniMessageUtil.mm.serialize(component) + val plainTransform = MiniMessageUtil.plain_text_mm.serialize(component) + if(transformed == plainTransform) return null + if(permission.onlyMinimessage()){ + return transformed + } - if ((!canUseColorCode) && (!canUseHexColor)) return false - var hasReversed = false + // smol dance so we transform the component that may contain other tag into only decoration & color for legacy + val coloredMessage = MiniMessageUtil.color_only_mm.deserialize(transformed) + val legacyMessage = StringBuilder(MiniMessageUtil.legacy_mm.serialize(coloredMessage)) // Reverse hex pattern - if (canUseHexColor) { - val nbReplacement = replaceColorToHex(colorToText, 14) - - if (nbReplacement > 0) hasReversed = true + if (permission.canUseHexColor) { + replaceColorToHex(legacyMessage, 14) } - if (canUseColorCode) { - replaceAll(colorToText, "&", "&&", 1) - val nbReplacement = replaceAll(colorToText, "§", "&", 2) - - if (nbReplacement > 0) hasReversed = true + // Reverse color pattern + if (permission.canUseColorCode) { + replaceAll(legacyMessage, "&", "&&", 1) + replaceAll(legacyMessage, "§", "&", 2) } - return hasReversed + // In case we still has some § around by lack of permission we need to convert it back from legacy + // In other word it's time for dance #3 + val fromLegacy = MiniMessageUtil.legacy_mm.deserialize(legacyMessage.toString()) + val middleGround = MiniMessageUtil.mm.serialize(fromLegacy) + val hackySolutionStb = StringBuilder(middleGround) + replaceAll(hackySolutionStb, "\\<", "<", 2) + val hackySolution = hackySolutionStb.toString() + + val result: String = + if(permission.canUseMinimessage) hackySolution + else MiniMessageUtil.mm.stripTags(hackySolution) + + return if(result == plainTransform) null + else result } /** @@ -123,7 +200,7 @@ object AnvilColorUtil { * @param endOffset Amount of character that should be ignored at the end. * @return The number of replacement was that was done. */ - private fun replaceHexToColor(builder: StringBuilder, endOffset: Int): Int { + private fun replaceHexToColor(builder: StringBuilder, endOffset: Int, checkTag: Boolean): Int { val matcher: Matcher = HEX_PATTERN.matcher(builder) var numberOfChanges = 0 @@ -132,6 +209,10 @@ object AnvilColorUtil { while (matcher.find(startIndex)) { startIndex = matcher.start() if (startIndex >= builder.length - endOffset) break //HOW AND WHERE WOULD THIS HAPPEN ????? + if(checkTag && isInTag(builder, startIndex)) { + startIndex += 1 // Avoid infinite loop + continue + } builder.replace(startIndex, startIndex + 1, "§x") startIndex += 2 @@ -146,6 +227,32 @@ object AnvilColorUtil { return numberOfChanges } + // Simple check if < > with some smart check like <> > not taken into account + // This is easily bypassable but if the player want to bypass he has better alternative + // AKA should avoid getting into any tag + private fun isInTag(builder: StringBuilder, index: Int): Boolean { + // Check left tag we have < after last > + val left = builder.slice(0..index) + val leftIndex = left.lastIndexOf("<") + var rightIndex = left.lastIndexOf(">") + + // last < do not exist or is before last > + if(leftIndex == -1 || rightIndex > leftIndex) return false + + val right = builder.slice(index..") + + // first > do not exist or is after first < (if exist) + if (rightIndex == -1 || (newleftIndex != -1 && newleftIndex < rightIndex)) return false + + // Then finally we use minimessage to check for tag + val expectedTag = builder.substring(leftIndex, newleftIndex + 1) + val notag = MiniMessageUtil.mm.stripTags(expectedTag) + + return notag != expectedTag + } + /** * Replace every hex color from the minecraft format to a format like #000000 * @param builder The builder to replace the minecraft hex color from. @@ -177,10 +284,11 @@ object AnvilColorUtil { enum class ColorUseType( val colorCodePerm: String?, - val hexColorPerm: String? + val hexColorPerm: String?, + val minimessagePerm: String? ) { - RENAME("ca.color.code", "ca.color.hex"), - LORE_EDIT(null, null) + RENAME("ca.color.code", "ca.color.hex", "ca.rename.minimessage"), + LORE_EDIT(null, null, null) } } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt index dd0da1e..36f0efb 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilLoreEditUtil.kt @@ -1,13 +1,18 @@ package xyz.alexcrea.cuanvil.util +import net.kyori.adventure.text.Component import org.bukkit.entity.HumanEntity import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.BookMeta import org.bukkit.permissions.Permissible import xyz.alexcrea.cuanvil.dependency.DependencyManager +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.componentLore +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentLore import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil import xyz.alexcrea.cuanvil.util.config.LoreEditType +import java.util.* import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicReference object AnvilLoreEditUtil { @@ -32,24 +37,24 @@ object AnvilLoreEditUtil { val result = first.clone() val meta = result.itemMeta ?: return null - val lore = if (meta.hasLore()) { - ArrayList(meta.lore!!) - } else ArrayList() + val lore = meta.componentLore() val page = book.pages[0] val lines = ArrayList(page.split("\n")) - val colorCost = colorLines(player, lines, LoreEditType.APPEND_BOOK) + val outLines = ArrayList(lines.size) + val colorCost = colorLines(player, LoreEditType.APPEND_BOOK, + lines, outLines) - lore.addAll(lines) + lore.addAll(outLines) - meta.lore = lore + meta.setComponentLore(lore) result.itemMeta = meta if (result == first) return null // Handle xp xpCost.addAndGet(colorCost) // Cost of using color - xpCost.addAndGet(lines.size * LoreEditType.APPEND_BOOK.perLineCost) // per line cost + xpCost.addAndGet(outLines.size * LoreEditType.APPEND_BOOK.perLineCost) // per line cost xpCost.addAndGet(baseEditLoreXpCost(first, result, LoreEditType.APPEND_BOOK)) // Fixed cost and work penalty return result @@ -61,7 +66,7 @@ object AnvilLoreEditUtil { // remove lore val result = first.clone() val leftMeta = result.itemMeta ?: return null - val currentLore: ArrayList = DependencyManager.stripLore(result) + val currentLore = DependencyManager.stripLore(result) if (currentLore.isEmpty()) return null val uncolorCost = uncolorLines(player, currentLore, LoreEditType.REMOVE_BOOK) @@ -112,10 +117,10 @@ object AnvilLoreEditUtil { } fun tryLoreEditByBook(player: HumanEntity, first: ItemStack, second: ItemStack, xpCost: AtomicInteger): ItemStack? { - val bookType = bookLoreEditIsAppend(first, second) ?: return null + val isAppend = bookLoreEditIsAppend(first, second) ?: return null val meta = second.itemMeta as BookMeta - return if (bookType) handleLoreAppendByBook(player, first, meta, xpCost) + return if (isAppend) handleLoreAppendByBook(player, first, meta, xpCost) else handleLoreRemoveByBook(player, first, xpCost) } @@ -148,24 +153,23 @@ object AnvilLoreEditUtil { val result = first.clone() val meta = result.itemMeta ?: return null - val lore = if (meta.hasLore()) { - ArrayList(meta.lore!!) - } else ArrayList() + val lore = meta.componentLore() val appendEnd = LoreEditConfigUtil.paperLoreOrderIsEnd // A bit overdone to color 1 line but hey - val tempList = ArrayList(1) - tempList.add(second.itemMeta!!.displayName) - val colorCost = colorLines(player, tempList, LoreEditType.APPEND_PAPER) + val outList = ArrayList(1) + val colorCost = colorLines(player, LoreEditType.APPEND_PAPER, + Collections.singletonList(second.itemMeta!!.displayName), + outList) - val line = tempList[0] + val line = outList[0] if (appendEnd) lore.add(line) else lore.add(0, line) - meta.lore = lore + meta.setComponentLore(lore) result.itemMeta = meta if (result == first) return null @@ -185,7 +189,7 @@ object AnvilLoreEditUtil { val meta = result.itemMeta!! val removeEnd = LoreEditConfigUtil.paperLoreOrderIsEnd - val lore: ArrayList = DependencyManager.stripLore(result) + val lore = DependencyManager.stripLore(result) if (lore.isEmpty()) return null val line = if (removeEnd) lore.removeAt(lore.size - 1) @@ -197,18 +201,16 @@ object AnvilLoreEditUtil { // Update lore but make sure custom lore is put last DependencyManager.updateLore(result) - val finalLore = ArrayList() - finalLore.addAll(meta.lore ?: emptyList()) + val finalLore = ArrayList() + finalLore.addAll(meta.componentLore()) finalLore.addAll(lore) - meta.lore = finalLore + meta.setComponentLore(finalLore) result.itemMeta = meta if (result == first) return null // Get color cost to uncolor this line - val tempList = ArrayList(1) - tempList.add(line) - val uncolorCost = uncolorLines(player, tempList, LoreEditType.REMOVE_PAPER) + val uncolorCost = uncolorLine(player, line, LoreEditType.REMOVE_PAPER) // Handle other xp xpCost.addAndGet(uncolorCost) @@ -223,9 +225,9 @@ object AnvilLoreEditUtil { second: ItemStack, xpCost: AtomicInteger ): ItemStack? { - val bookType = paperLoreEditIsAppend(first, second) ?: return null + val isAppend = paperLoreEditIsAppend(first, second) ?: return null - return if (bookType) handleLoreAppendByPaper(player, first, second, xpCost) + return if (isAppend) handleLoreAppendByPaper(player, first, second, xpCost) else handleLoreRemoveByPaper(player, first, xpCost) } @@ -240,55 +242,69 @@ object AnvilLoreEditUtil { return xpCost } - private fun colorLines(player: Permissible, lines: ArrayList, editType: LoreEditType): Int { - val canUseHex = editType.allowHexColor - val canUseColorCode = editType.allowColorCode + fun colorPermission(player: Permissible, editType: LoreEditType): AnvilColorUtil.ColorPermissions { + return AnvilColorUtil.calculatePermissions(player, + false, + editType.allowColorCode, + editType.allowHexColor, + editType.allowMinimessage, + AnvilColorUtil.ColorUseType.LORE_EDIT) + } + + private fun colorLine(line: String, permission: AnvilColorUtil.ColorPermissions): Component? { + return AnvilColorUtil.handleColor( + line, + permission + ) + } + + private fun colorLines(player: Permissible, editType: LoreEditType, + lines: List, outLines: MutableList): Int { + val permission = colorPermission(player, editType) val colorCost = editType.useColorCost - // Now handle color of each lines + // Handle color and minimessage of each lines var hasUsedColor = false - for ((index, line) in lines.withIndex()) { - val coloredLine = StringBuilder(line) + for (line in lines) { + val component = colorLine(line, permission) - val lineUsedColor = AnvilColorUtil.handleColor( - coloredLine, - player, - false, canUseColorCode, canUseHex, - AnvilColorUtil.ColorUseType.LORE_EDIT - ) - - if (lineUsedColor) { + if (component != null) { hasUsedColor = true - lines[index] = coloredLine.toString() + outLines.add(component) + } else { + outLines.add(Component.text(line)) } } - return if (hasUsedColor) { - colorCost - } else { - 0 - } + return if (hasUsedColor) colorCost + else 0 } - fun uncolorLines(player: Permissible, lines: ArrayList, editType: LoreEditType): Int { - if (!editType.shouldRemoveColorOnLoreRemoval) return 0 + fun uncolorLines(player: Permissible, lines: MutableList, editType: LoreEditType): Int { + val permission = colorPermission(player, editType) // Now handle color of each lines var hasUndidColor = false for ((index, line) in lines.withIndex()) { - val uncoloredLine = StringBuilder(line) + if(line == null){ + lines[index] = null + continue + } - val lineUndidColor = AnvilColorUtil.revertColor( - uncoloredLine, - player, - false, true, true, - AnvilColorUtil.ColorUseType.LORE_EDIT + val clearedLine = AnvilColorUtil.revertColorSmallest( + line, + permission ) - if (lineUndidColor) { + val result: String + if (clearedLine != null) { hasUndidColor = true - lines[index] = uncoloredLine.toString() + result = clearedLine + } else { + result = MiniMessageUtil.plain_text_mm.serialize(line) } + + lines[index] = MiniMessageUtil.plain_text_mm.deserialize(result) } return if (hasUndidColor) { @@ -298,4 +314,36 @@ object AnvilLoreEditUtil { } } + // do not output the uncolored line... + fun uncolorLine(player: Permissible, line: Component?, editType: LoreEditType): Int { + return uncolorLine(player, AtomicReference(line), editType) + } + + fun uncolorLine(player: Permissible, line: AtomicReference, editType: LoreEditType): Int { + val coloredComponent = line.get() ?: return 0 + val permission = colorPermission(player, editType) + + val clearedLine = AnvilColorUtil.revertColorSmallest( + coloredComponent, + permission + ) + + var hasUndidColor = false + val result: String + if(clearedLine != null){ + hasUndidColor = true + result = clearedLine + } else { + // Remove extra tags + result = MiniMessageUtil.plain_text_mm.serialize(coloredComponent) + } + line.set(MiniMessageUtil.plain_text_mm.deserialize(result)) + + return if (hasUndidColor) { + editType.removeColorCost + } else { + 0 + } + } + } \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/MiniMessageUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/MiniMessageUtil.kt new file mode 100644 index 0000000..c33cb9c --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/MiniMessageUtil.kt @@ -0,0 +1,33 @@ +package xyz.alexcrea.cuanvil.util + +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.minimessage.MiniMessage +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver +import net.kyori.adventure.text.minimessage.tag.standard.StandardTags +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer +import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil + +object MiniMessageUtil { + + val color_only_mm = MiniMessage.builder() + .tags( + TagResolver.resolver( + StandardTags.color(), + StandardTags.decorations() + ) + ) + .build() + + val mm = if (PlatformUtil.isPaper) MiniMessage.miniMessage() + else color_only_mm + + val legacy_mm = LegacyComponentSerializer.legacySection() + val plain_text_mm = PlainTextComponentSerializer.plainText() + + // Keeping track of this as most use of this can be replaced later on v2 with pure component alternative + fun fromLegacy(legacyText: String): TextComponent { + return legacy_mm.deserialize(legacyText) + } + +} diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt index f5758dc..9d0eb6a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditConfigUtil.kt @@ -17,9 +17,9 @@ object LoreEditConfigUtil { // Color configs path const val ALLOW_COLOR_CODE = "allow_color_code" const val ALLOW_HEX_COLOR = "allow_hexadecimal_color" + const val ALLOW_MINIMESSAGE = "allow_minimessage" const val USE_COLOR_COST = "use_cost" - const val REMOVE_COLOR_ON_LORE_REMOVE = "remove_color_on_remove" const val REMOVE_COLOR_COST = "remove_color_cost" // Lore order config path @@ -42,9 +42,9 @@ object LoreEditConfigUtil { // Color configs defaults const val DEFAULT_ALLOW_COLOR_CODE = true const val DEFAULT_ALLOW_HEX_COLOR = true + const val DEFAULT_ALLOW_MINIMESSAGE = true const val DEFAULT_USE_COLOR_COST = 0 - const val DEFAULT_REMOVE_COLOR_ON_LORE_REMOVE = false const val DEFAULT_REMOVE_COLOR_COST = 0 // Lore order config default diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt index ed5ef1b..8bd926a 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/config/LoreEditType.kt @@ -3,14 +3,14 @@ package xyz.alexcrea.cuanvil.util.config import xyz.alexcrea.cuanvil.util.AnvilUseType import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_COLOR_CODE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_HEX_COLOR +import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.ALLOW_MINIMESSAGE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_ALLOW_COLOR_CODE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_ALLOW_HEX_COLOR +import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_ALLOW_MINIMESSAGE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_REMOVE_COLOR_COST -import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_REMOVE_COLOR_ON_LORE_REMOVE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_USE_COLOR_COST import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.REMOVE_COLOR_COST import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.REMOVE_COLOR_COST_RANGE -import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.REMOVE_COLOR_ON_LORE_REMOVE import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.USE_COLOR_COST import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.USE_COLOR_COST_RANGE import xyz.alexcrea.cuanvil.config.ConfigHolder.DEFAULT_CONFIG as CONFIG @@ -79,27 +79,35 @@ enum class LoreEditType( } /** - * Allow usage of color code on lore add + * Allow usage or removal of color code */ val allowColorCode: Boolean get() { - if (!isAppend) throw IllegalStateException("Can only call with an append edit type") return CONFIG .config .getBoolean("$rootPath.$ALLOW_COLOR_CODE", DEFAULT_ALLOW_COLOR_CODE) } /** - * Allow usage of hexadecimal color on lore add + * Allow usage or removal of hexadecimal color */ val allowHexColor: Boolean get() { - if (!isAppend) throw IllegalStateException("Can only call with an append edit type") return CONFIG .config .getBoolean("${rootPath}.$ALLOW_HEX_COLOR", DEFAULT_ALLOW_HEX_COLOR) } + /** + * Allow usage or removal of minimessage on lore add + */ + val allowMinimessage: Boolean + get() { + return CONFIG + .config + .getBoolean("${rootPath}.$ALLOW_MINIMESSAGE", DEFAULT_ALLOW_MINIMESSAGE) + } + /** * Cost when using either color code and hex color on lore add */ @@ -114,17 +122,6 @@ enum class LoreEditType( } - /** - * Should the color code & hex color should get removed on lore remove - */ - val shouldRemoveColorOnLoreRemoval: Boolean - get() { - if (isAppend) throw IllegalStateException("Can only call with a remove edit type") - return CONFIG - .config - .getBoolean("${rootPath}.$REMOVE_COLOR_ON_LORE_REMOVE", DEFAULT_REMOVE_COLOR_ON_LORE_REMOVE) - } - /** * Cost when using either color code and hex color on lore remove */ diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index a0f0876..5476ec7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -59,8 +59,12 @@ sacrifice_illegal_enchant_cost: 1 # # Color code are prefixed by "&" and hexadecimal color by "#". # Color code will not be applied if it colors nothing. "&&" can be used to write "&". +# For minimessage search for minimessage formating https://docs.papermc.io/adventure/minimessage/format/ +# Note that only color and decoration tags are allowed for minimisage in the v1 version of this plugin +# but any global tag will be allowed later when v2 release allow_color_code: false allow_hexadecimal_color: false +allow_minimessage: false # Toggle if color should only be applicable if the player a certain permission. # @@ -301,9 +305,12 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true - use_cost: 0 + allow_hexadecimal_color: false + allow_minimessage: true remove: # If removing lore using book & quil is enabled @@ -318,16 +325,25 @@ lore_edit: shared_additive: false # If removing the lore consume the book & quil do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this book should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true paper: # Permission is ca.lore_edit.paper use_permission: true # what order should the lines should get added/removed (start/end, if invalid or not present will be end) - order: "end" + order: end append_line: # If adding lore line using paper is enabled @@ -344,8 +360,12 @@ lore_edit: # # Color code are prefixed by "&" and hexadecimal color by "#" # Color code will not be applied if it colors nothing. "&&" can be used to write "&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + # + # Note that currently minimessage would disable hex code when adding color allow_color_code: true - allow_hexadecimal_color: true + allow_hexadecimal_color: false + allow_minimessage: true color_use_cost: 0 remove_line: @@ -359,10 +379,19 @@ lore_edit: shared_additive: false # If removing the lore line consume the paper do_consume: false - # If the color should get back to color code or hex format - remove_color_on_remove: true # Cost of replacing colors remove_color_cost: 0 + # Allowed some color and tags to be reverted to plain text + # Custom anvil will prioritise format that result is a smaller resulting text + # Note that not allowing certain format will lead to some lost of color or tags. + # If configuration are exact as append appending this paper should result in the exact same color + # + # Color code will be prefixed by "&" and hexadecimal color by "#". + # If color code is allowed, "&" in the text will get converted to "&&" + # For minimessage see minimessage formating https://docs.papermc.io/adventure/minimessage/format/ + allow_color_code: true + allow_hexadecimal_color: false + allow_minimessage: true # Whether to show debug logging debug_log: false diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fc083a6..4532d49 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -46,6 +46,9 @@ permissions: ca.color.hex: default: op description: Allow player to use hexadecimal color if enabled (toggleable) + ca.rename.minimessage: + default: op + description: Allow player to use minimessage formating on rename if enabled (toggleable) (only legacy compatible at the time) # lore edit permissions ca.lore_edit.book: default: op diff --git a/src/test/java/xyz/alexcrea/cuanvil/anvil/LoreEditTests.java b/src/test/java/xyz/alexcrea/cuanvil/anvil/LoreEditTests.java index ebfb84f..33b0f35 100644 --- a/src/test/java/xyz/alexcrea/cuanvil/anvil/LoreEditTests.java +++ b/src/test/java/xyz/alexcrea/cuanvil/anvil/LoreEditTests.java @@ -289,9 +289,9 @@ public class LoreEditTests extends SharedCustomAnvilTest { if (type.isAppend()) { ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.ALLOW_HEX_COLOR, true); ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.ALLOW_COLOR_CODE, true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.ALLOW_MINIMESSAGE, true); ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.USE_COLOR_COST, 0); } else { - ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.REMOVE_COLOR_ON_LORE_REMOVE, false); ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.REMOVE_COLOR_COST, 0); } @@ -437,7 +437,6 @@ public class LoreEditTests extends SharedCustomAnvilTest { public void testColorCost(LoreEditType type) { ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.USE_COLOR_COST, COLOR_USE_COST); ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.REMOVE_COLOR_COST, COLOR_REMOVE_COST); - ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.REMOVE_COLOR_ON_LORE_REMOVE, true); TestDataContainer singleLData = singleLineTypeToTest.get(type); TestDataContainer multiLData = multiLineTypeToTest.get(type); @@ -481,8 +480,6 @@ public class LoreEditTests extends SharedCustomAnvilTest { @ParameterizedTest @MethodSource("onlyRemoveTypes") public void testColorRemoveEnabled(LoreEditType type) { - ConfigHolder.DEFAULT_CONFIG.getConfig().set(type.getRootPath() + "." + LoreEditConfigUtil.REMOVE_COLOR_ON_LORE_REMOVE, true); - TestDataContainer singleLData = singleLineTypeToTest.get(type); TestDataContainer multiLData = multiLineTypeToTest.get(type);