progress on using pure component in paper

This commit is contained in:
alexcrea 2025-10-23 14:26:11 +02:00
parent 2967d500eb
commit 11f7bf8602
Signed by: alexcrea
GPG key ID: E346CD16413450E3
17 changed files with 502 additions and 167 deletions

View file

@ -304,6 +304,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -321,10 +323,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
paper:
# Permission is ca.lore_edit.paper
@ -348,6 +361,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -364,10 +379,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
# Whether to show debug logging
debug_log: false

View file

@ -316,6 +316,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -333,10 +335,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
paper:
# Permission is ca.lore_edit.paper
@ -360,6 +373,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -376,10 +391,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
# Whether to show debug logging
debug_log: false

View file

@ -304,6 +304,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -321,10 +323,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
paper:
# Permission is ca.lore_edit.paper
@ -348,6 +361,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -364,10 +379,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
# Whether to show debug logging
debug_log: false

View file

@ -1,10 +1,35 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = rootProject.group
version = rootProject.version
plugins {
id("io.papermc.paperweight.userdev")
}
dependencies {
// Spigot api
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
// Used for nms
paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT")
// Protocolib
compileOnly("net.dmulloy2:ProtocolLib:5.4.0")
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Set target version
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = "21"
targetCompatibility = "21"
options.encoding = "UTF-8"
}
kotlin {
compilerOptions {
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
jvmTarget.set(JvmTarget.JVM_21)
}
}

View file

@ -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<Component> {
val lore: List<Component>?
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<Component?>) {
if(isPaper){
this.lore(lore)
} else {
val legacyLore = ArrayList<String?>(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)
}
}
}

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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("")

View file

@ -2,10 +2,10 @@ 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
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareAnvilEvent
import org.bukkit.inventory.AnvilInventory
@ -24,13 +24,14 @@ 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
object DependencyManager {
var isFolia: Boolean = false
lateinit var scheduler: TaskScheduler
lateinit var packetManager: PacketManager
var externGuiTester: ExternGuiTester? = null
@ -50,8 +51,7 @@ object DependencyManager {
val pluginManager = Bukkit.getPluginManager()
// Bukkit or Paper scheduler ?
isFolia = testIsFolia()
scheduler = if (isFolia) {
scheduler = if (PlatformUtil.isFolia) {
CustomAnvil.instance.logger.info("Folia detected... Custom Anvil Folia support is experimental. issues are more likely to happens.")
FoliaScheduler()
@ -305,15 +305,15 @@ object DependencyManager {
return bypass
}
fun stripLore(item: ItemStack): ArrayList<String> {
val lore = ArrayList<String>()
fun stripLore(item: ItemStack): MutableList<Component?> {
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<Component?>()
lore.addAll(itemLore)
return lore
}
@ -321,13 +321,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
}
}
}

View file

@ -17,6 +17,7 @@ import org.bukkit.inventory.InventoryView
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.BookMeta
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
@ -30,6 +31,7 @@ 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
class AnvilResultListener : Listener {
@ -397,8 +399,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
@ -443,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?
@ -456,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
}
@ -489,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<String>(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
}

View file

@ -9,20 +9,33 @@ 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 or if minimessage formating was applied
*/
fun handleColor(
textToColorText: String,
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
): Component? {
if (!allowColorCode && !allowHexadecimalColor && !allowMinimessage) return null
useType: ColorUseType): ColorPermissions {
if (!allowColorCode && !allowHexadecimalColor && !allowMinimessage)
return ColorPermissions(
canUseColorCode = false,
canUseHexColor = false,
canUseMinimessage = false
)
val canUseColorCode =
allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission(
@ -37,19 +50,49 @@ object AnvilColorUtil {
useType.minimessagePerm
))
if (!canUseColorCode && !canUseHexColor && !canUseMinimessage) return null
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) { // maybe should use LegacyComponentSerializer ?
if (permission.canUseColorCode) { // maybe should use LegacyComponentSerializer ?
var nbReplacement = replaceAll(textToColor, "&", "§", 2)
nbReplacement -= 2 * replaceAll(textToColor, "§§", "&", 2)
if (nbReplacement > 0) useColor = true
}
if (canUseHexColor) {
if (permission.canUseHexColor) {
val nbReplacement = replaceHexToColor(textToColor, 7)
if (nbReplacement > 0) useColor = true
@ -57,8 +100,8 @@ object AnvilColorUtil {
val previousStr = textToColor.toString()
var result: Component = MiniMessageUtil.legacy_mm.deserialize(previousStr)
if(canUseMinimessage) {
// we dance with formats here
if(permission.canUseMinimessage) {
// we dance with formats here TODO maybe extract, if possible, only the "text" part and use it for compare with previous as tag would be missing?
val toMinimessage = MiniMessageUtil.mm.serialize(result)
val hackySolution = toMinimessage.replace("\\<", "<")
val fromMinimessage = MiniMessageUtil.mm.deserialize(hackySolution)
@ -75,46 +118,72 @@ object AnvilColorUtil {
}
/**
* 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 color type, color use type and player permissions
* @return the new component if had any change. null otherwise
*/
fun revertColor(
colorToText: StringBuilder,
fun revertColorSmallest(
component: Component,
player: Permissible,
usePermission: Boolean,
allowColorCode: Boolean,
allowMinimessage: Boolean,
allowHexadecimalColor: Boolean,
useType: ColorUseType
): Boolean {
if (!allowColorCode && !allowHexadecimalColor) return false
): String? {
val permission = calculatePermissions(player, usePermission,
allowColorCode, allowHexadecimalColor, allowMinimessage,
useType)
return revertColorSmallest(component, permission)
}
val canUseColorCode =
allowColorCode && (!usePermission || useType.colorCodePerm == null || player.hasPermission(
useType.colorCodePerm
))
val canUseHexColor =
allowHexadecimalColor && (!usePermission || useType.hexColorPerm == null || player.hasPermission(
useType.hexColorPerm
))
/**
* 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 revertColorSmallest(
component: Component?,
permission: ColorPermissions
): String? {
if(!permission.allowed() || component == null) return null
if ((!canUseColorCode) && (!canUseHexColor)) return false
var hasReversed = false
val transformed = MiniMessageUtil.mm.serialize(component)
val plainTransform = MiniMessageUtil.plain_text_mm.serialize(component)
if(transformed == plainTransform) return null
if(permission.onlyMinimessage()){
return transformed
}
// 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.color_only_mm.serialize(fromLegacy)
val hackySolution = middleGround.replace("\\<", "<")
val result: String =
if(permission.canUseMinimessage) hackySolution
else MiniMessageUtil.mm.stripTags(hackySolution)
return if(result == plainTransform) null
else result
}
/**

View file

@ -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<String>(meta.lore!!)
} else ArrayList()
val lore = meta.componentLore()
val page = book.pages[0]
val lines = ArrayList<String>(page.split("\n"))
val colorCost = colorLines(player, lines, LoreEditType.APPEND_BOOK)
val outLines = ArrayList<Component>(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<String> = DependencyManager.stripLore(result)
val currentLore = DependencyManager.stripLore(result)
if (currentLore.isEmpty()) return null
val uncolorCost = uncolorLines(player, currentLore, LoreEditType.REMOVE_BOOK)
@ -148,24 +153,23 @@ object AnvilLoreEditUtil {
val result = first.clone()
val meta = result.itemMeta ?: return null
val lore = if (meta.hasLore()) {
ArrayList<String>(meta.lore!!)
} else ArrayList()
val lore = meta.componentLore()
val appendEnd = LoreEditConfigUtil.paperLoreOrderIsEnd
// A bit overdone to color 1 line but hey
val tempList = ArrayList<String>(1)
tempList.add(second.itemMeta!!.displayName)
val colorCost = colorLines(player, tempList, LoreEditType.APPEND_PAPER)
val outList = ArrayList<Component>(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<String> = 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<String>()
finalLore.addAll(meta.lore ?: emptyList())
val finalLore = ArrayList<Component?>()
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<String>(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)
@ -240,26 +242,37 @@ object AnvilLoreEditUtil {
return xpCost
}
private fun colorLines(player: Permissible, lines: ArrayList<String>, editType: LoreEditType): Int {
val canUseHex = editType.allowHexColor
val canUseColorCode = editType.allowColorCode
val minimessage = editType.allowMinimessage
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<String>, outLines: MutableList<Component>): Int {
val permission = colorPermission(player, editType)
val colorCost = editType.useColorCost
// Handle color and minimessage of each lines
var hasUsedColor = false
for ((index, line) in lines.withIndex()) {
val component = AnvilColorUtil.handleColor(
line,
player,
false,
canUseColorCode, canUseHex, minimessage,
AnvilColorUtil.ColorUseType.LORE_EDIT
)
for (line in lines) {
val component = colorLine(line, permission)
if (component != null) {
hasUsedColor = true
lines[index] = MiniMessageUtil.legacy_mm.serialize(component)
outLines.add(component)
} else {
outLines.add(Component.text(line))
}
}
@ -267,25 +280,31 @@ object AnvilLoreEditUtil {
else 0
}
fun uncolorLines(player: Permissible, lines: ArrayList<String>, editType: LoreEditType): Int {
if (!editType.shouldRemoveColorOnLoreRemoval) return 0
fun uncolorLines(player: Permissible, lines: MutableList<Component?>, 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) {
@ -295,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<Component?>, 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
}
}
}

View file

@ -1,18 +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 mm = MiniMessage.builder()
.tags(TagResolver.resolver(
val color_only_mm = MiniMessage.builder()
.tags(
TagResolver.resolver(
StandardTags.color(),
StandardTags.decorations()))
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)
}
}

View file

@ -20,7 +20,6 @@ object LoreEditConfigUtil {
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
@ -46,7 +45,6 @@ object LoreEditConfigUtil {
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

View file

@ -8,11 +8,9 @@ import xyz.alexcrea.cuanvil.util.config.LoreEditConfigUtil.DEFAULT_ALLOW_COLOR_C
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
@ -81,33 +79,30 @@ 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 of minimessage on lore add
* Allow usage or removal of minimessage on lore add
*/
val allowMinimessage: Boolean
get() {
if (!isAppend) throw IllegalStateException("Can only call with an append edit type")
return CONFIG
.config
.getBoolean("${rootPath}.$ALLOW_MINIMESSAGE", DEFAULT_ALLOW_MINIMESSAGE)
@ -127,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
*/

View file

@ -60,6 +60,8 @@ 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
@ -304,6 +306,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -321,10 +325,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
paper:
# Permission is ca.lore_edit.paper
@ -348,6 +363,8 @@ 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 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: true
allow_hexadecimal_color: true
allow_minimessage: true
@ -364,10 +381,21 @@ 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/
# 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: true
allow_hexadecimal_color: true
allow_minimessage: true
# Whether to show debug logging
debug_log: false

View file

@ -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);