From 30ea9e5650034576efcb44e9a7bcc0664a511cbd Mon Sep 17 00:00:00 2001 From: Boy Date: Fri, 12 Jan 2024 23:01:49 +0100 Subject: [PATCH] refactor: restructure codebase, serialize stuff as little as possible --- .../com/mineinabyss/chatty/ChattyCommands.kt | 13 ++- .../chatty/components/ChattyNickname.kt | 3 +- .../mineinabyss/chatty/helpers/ChatHelpers.kt | 97 +++---------------- .../mineinabyss/chatty/helpers/SkinHelpers.kt | 63 ++++++++++++ .../chatty/listeners/ChatListener.kt | 2 +- .../chatty/listeners/PlayerListener.kt | 9 +- .../com/mineinabyss/chatty/tags/ChattyTags.kt | 6 +- 7 files changed, 96 insertions(+), 97 deletions(-) create mode 100644 chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/SkinHelpers.kt diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyCommands.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyCommands.kt index e248f97..5d359da 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyCommands.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/ChattyCommands.kt @@ -22,6 +22,7 @@ import io.papermc.paper.event.player.AsyncChatDecorateEvent import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.withContext +import net.kyori.adventure.text.Component import org.bukkit.Bukkit import org.bukkit.command.Command import org.bukkit.command.CommandSender @@ -334,14 +335,16 @@ class ChattyCommands : IdofrontCommandExecutor(), TabCompleter { companion object { private fun CommandSender.sendFormattedMessage(message: String, optionalPlayer: Player? = null) = - this.sendMessage(translatePlaceholders((optionalPlayer ?: this as? Player), message).parseTags(this as? Player, true)) + (optionalPlayer ?: this as? Player)?.let { player -> + this.sendMessage(translatePlaceholders(player, message).miniMsg(player.buildTagResolver(true))) + } private fun Player.sendFormattedPrivateMessage(messageFormat: String, message: String, receiver: Player) = - this.sendMessage( - (translatePlaceholders(receiver, messageFormat).serialize() + message) - .parseTags(receiver, true) + this.sendMessage(Component.textOfChildren( + translatePlaceholders(receiver, messageFormat).miniMsg(receiver.buildTagResolver(true)), + message.miniMsg(receiver.buildTagResolver(true))) ) - private fun CommandSender.sendConsoleMessage(message: String) = this.sendMessage(message.parseTags(null, true)) + private fun CommandSender.sendConsoleMessage(message: String) = this.sendMessage(message.miniMsg().parseTags(null, true)) private fun List.removeFirstArgumentOfStringList(): String = this.filter { it != this.first() }.toSentence() diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyNickname.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyNickname.kt index 47ffad6..9b84691 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyNickname.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/components/ChattyNickname.kt @@ -3,6 +3,7 @@ package com.mineinabyss.chatty.components import com.mineinabyss.chatty.chatty import com.mineinabyss.chatty.helpers.parseTags import com.mineinabyss.geary.papermc.tracking.entities.toGeary +import com.mineinabyss.idofront.textcomponents.miniMsg import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import org.bukkit.entity.Player @@ -13,6 +14,6 @@ data class ChattyNickname(val nickname: String) var Player.chattyNickname get() = this.toGeary().get()?.nickname set(value) = this.toGeary().run { - if (chatty.config.nicknames.useDisplayName) this@chattyNickname.displayName(value?.parseTags(this@chattyNickname) ?: name()) + if (chatty.config.nicknames.useDisplayName) this@chattyNickname.displayName(value?.miniMsg()?.parseTags(this@chattyNickname) ?: name()) value?.let { setPersisting(ChattyNickname(it)) } ?: remove() } diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt index 49bc14f..16b388a 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/ChatHelpers.kt @@ -42,7 +42,6 @@ import org.bukkit.inventory.ItemStack import org.bukkit.profile.PlayerTextures.SkinModel import java.util.regex.Pattern -val gson = GsonComponentSerializer.gson() val getAlternativePingSounds: List = chatty.config.ping.let { ping -> if ("*" in ping.alternativePingSounds || "all" in ping.alternativePingSounds) Sound.entries.map { it.key.toString() }.toList() else ping.alternativePingSounds } @@ -54,21 +53,12 @@ fun String.checkForPlayerPings(channelId: String): Player? { val ping = chatty.config.ping if (channelId !in getPingEnabledChannels || ping.pingPrefix.isEmpty() || ping.pingPrefix !in this) return null val pingedName = this.substringAfter(ping.pingPrefix).substringBefore(" ") - return Bukkit.getOnlinePlayers().firstOrNull { player -> - player.name == pingedName || player.chattyNickname?.stripTags() == pingedName - } + return Bukkit.getOnlinePlayers().firstOrNull { it.name == pingedName } } val emptyMiniMessage = MiniMessage.builder().tags(TagResolver.empty()).build() - -/** Build a unique instance of MiniMessage with an empty TagResolver and deserializes with a generated one that takes permissions into account - * @param player Format tags based on a player's permission, or null to parse all tags - * @param ignorePermissions Whether to ignore permissions and parse all tags - */ -fun String.parseTags(player: Player? = null, ignorePermissions: Boolean = false): Component { - val mm = if (ignorePermissions) MiniMessage.miniMessage() else emptyMiniMessage - return mm.deserialize(this.fixSerializedTags(), player.buildTagResolver(ignorePermissions)) -} +val miniMessage = MiniMessage.builder().tags(TagResolver.standard()).build() +val gson = GsonComponentSerializer.gson() fun Player?.buildTagResolver(ignorePermissions: Boolean = false): TagResolver { val tagResolver = TagResolver.builder() @@ -87,8 +77,12 @@ fun Player?.buildTagResolver(ignorePermissions: Boolean = false): TagResolver { return tagResolver.build() } -fun Component.parseTags(player: Player? = null, ignorePermissions: Boolean = false) = - this.serialize().parseTags(player, ignorePermissions) +//TODO This breaks our custom tags, due to component, Component.text(": hi", NamedTextColor.RED), being serialized to "\: hi" +// Thus when deserializing, even with the tag resolver, it assumes the tag is escaped +fun Component.parseTags(player: Player? = null, ignorePermissions: Boolean = false) : Component { + val mm = if (ignorePermissions) miniMessage else emptyMiniMessage + return mm.deserialize(this.serialize().fixSerializedTags(), player.buildTagResolver(ignorePermissions)) +} fun Component.removeTrailingSpaces() = this.replaceText(TextReplacementConfig.builder().match(" +\$").replacement("").build()) @@ -106,69 +100,9 @@ fun getDefaultChat() = ?: getGlobalChat() ?: throw IllegalStateException("No Default or Global channel found") -// TODO change to data.channel -//fun getChannelFromId(channelId: String) = -// chatty.config.channels[channelId] - -//fun Player.getChannelFromPlayer() = -// chatty.config.channels.entries.firstOrNull { it.key == this.chattyData.channelId }?.value - fun getAllChannelNames() = chatty.config.channels.keys.toList() -fun translatePlaceholders(player: Player?, message: String): Component { - return PlaceholderAPI.setPlaceholders(player, message).fixLegacy() -} - -val playerHeadMapCache = mutableMapOf() -fun OfflinePlayer.translatePlayerHeadComponent(): Component { - if (this !in playerHeadMapCache || playerHeadMapCache[this]!!.font() != Key.key(chatty.config.playerHeadFont)) { - playerHeadMapCache[this] = runCatching { getPlayerHeadTexture(ascent = -5) }.getOrDefault(Component.empty()) - } - return playerHeadMapCache[this] ?: Component.empty() -} - -val playerBodyMapCache = mutableMapOf() -fun Player.refreshSkinInCaches() { - playerBodyMapCache -= this - playerHeadMapCache -= this -} -fun OfflinePlayer.translateFullPlayerSkinComponent(): Component { - if (this !in playerBodyMapCache || playerBodyMapCache[this]!!.font() != Key.key(chatty.config.playerHeadFont)) { - playerBodyMapCache[this] = runCatching { getFullPlayerBodyTexture(ascent = -5) }.getOrDefault(Component.empty()) - } - return playerBodyMapCache[this] ?: Component.empty() -} - -fun OfflinePlayer.getPlayerHeadTexture( - scale: Int = 1, - ascent: Int = 0, - colorType: ColorType = ColorType.MINIMESSAGE, - font: Key = Key.key(chatty.config.playerHeadFont) -): Component { - val image = avatarBuilder(this, scale, ascent, colorType).getBodyBufferedImage(scale).getSubimage(4, 0, 8, 8) - return "${ImageUtils.generateStringFromImage(image, colorType, ascent)}".miniMsg() -} - -fun OfflinePlayer.getFullPlayerBodyTexture( - scale: Int = 1, - ascent: Int = 0, - colorType: ColorType = ColorType.MINIMESSAGE, - font: Key = Key.key(chatty.config.playerHeadFont) -): Component { - val image = avatarBuilder(this, scale, ascent, colorType).getBodyBufferedImage(scale) - return "${ImageUtils.generateStringFromImage(image, colorType, ascent)}".miniMsg() -} - -private fun avatarBuilder( - player: OfflinePlayer, - scale: Int = 1, - ascent: Int = 0, - colorType: ColorType = ColorType.MINIMESSAGE -): Avatar { - return Avatar.builder().isSlim(player.playerProfile.apply { this.update() }.textures.skinModel == SkinModel.SLIM) - .playerName(player.name) - .ascent(ascent).colorType(colorType).scale(scale).build() -} +fun translatePlaceholders(player: Player?, message: String) = PlaceholderAPI.setPlaceholders(player, message) fun String.fixSerializedTags(): String = this.replaceAll("\\\\(?!u)(?!\")(?!:)", "") @@ -184,19 +118,16 @@ fun List.toSentence() = this.joinToString(" ") fun String.toPlayer() = Bukkit.getPlayer(this) fun Player.sendFormattedMessage(message: String) = - this.sendMessage(translatePlaceholders(this, message).parseTags(player, true)) + this.sendMessage(translatePlaceholders(this, message).miniMsg(buildTagResolver(true))) fun Player.sendFormattedMessage(vararg message: String, optionalPlayer: Player? = null) = this.sendMessage( - translatePlaceholders((optionalPlayer ?: this), message.joinToString(" ")).parseTags( - optionalPlayer ?: this, - true - ) + translatePlaceholders((optionalPlayer ?: this), message.joinToString(" ")).miniMsg((optionalPlayer ?: this).buildTagResolver(true)) ) fun appendChannelFormat(message: Component, player: Player, channel: ChattyChannel): Component { - val parsedFormat = translatePlaceholders(player, channel.format).parseTags(player, true) - val parsedMessage = Component.empty().color(channel.messageColor).append(message.parseTags(player, false)) + val parsedFormat = translatePlaceholders(player, channel.format).miniMsg(player.buildTagResolver(true)) + val parsedMessage = Component.empty().color(channel.messageColor).append(message) return parsedFormat.compact().append(parsedMessage) } diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/SkinHelpers.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/SkinHelpers.kt new file mode 100644 index 0000000..7811f66 --- /dev/null +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/helpers/SkinHelpers.kt @@ -0,0 +1,63 @@ +package com.mineinabyss.chatty.helpers + +import com.combimagnetron.imageloader.Avatar +import com.combimagnetron.imageloader.Image +import com.combimagnetron.imageloader.ImageUtils +import com.mineinabyss.chatty.chatty +import com.mineinabyss.idofront.textcomponents.miniMsg +import net.kyori.adventure.key.Key +import net.kyori.adventure.text.Component +import org.bukkit.OfflinePlayer +import org.bukkit.entity.Player +import org.bukkit.profile.PlayerTextures + +val playerHeadMapCache = mutableMapOf() +fun OfflinePlayer.translatePlayerHeadComponent(): Component { + if (this !in playerHeadMapCache || playerHeadMapCache[this]!!.font() != Key.key(chatty.config.playerHeadFont)) { + playerHeadMapCache[this] = runCatching { getPlayerHeadTexture(ascent = -5) }.getOrDefault(Component.empty()) + } + return playerHeadMapCache[this] ?: Component.empty() +} + +val playerBodyMapCache = mutableMapOf() +fun Player.refreshSkinInCaches() { + playerBodyMapCache -= this + playerHeadMapCache -= this +} +fun OfflinePlayer.translateFullPlayerSkinComponent(): Component { + if (this !in playerBodyMapCache || playerBodyMapCache[this]!!.font() != Key.key(chatty.config.playerHeadFont)) { + playerBodyMapCache[this] = runCatching { getFullPlayerBodyTexture(ascent = -5) }.getOrDefault(Component.empty()) + } + return playerBodyMapCache[this] ?: Component.empty() +} + +fun OfflinePlayer.getPlayerHeadTexture( + scale: Int = 1, + ascent: Int = 0, + colorType: Image.ColorType = Image.ColorType.MINIMESSAGE, + font: Key = Key.key(chatty.config.playerHeadFont) +): Component { + val image = avatarBuilder(this, scale, ascent, colorType).getBodyBufferedImage(scale).getSubimage(4, 0, 8, 8) + return "${ImageUtils.generateStringFromImage(image, colorType, ascent)}".miniMsg() +} + +fun OfflinePlayer.getFullPlayerBodyTexture( + scale: Int = 1, + ascent: Int = 0, + colorType: Image.ColorType = Image.ColorType.MINIMESSAGE, + font: Key = Key.key(chatty.config.playerHeadFont) +): Component { + val image = avatarBuilder(this, scale, ascent, colorType).getBodyBufferedImage(scale) + return "${ImageUtils.generateStringFromImage(image, colorType, ascent)}".miniMsg() +} + +private fun avatarBuilder( + player: OfflinePlayer, + scale: Int = 1, + ascent: Int = 0, + colorType: Image.ColorType = Image.ColorType.MINIMESSAGE +): Avatar { + return Avatar.builder().isSlim(player.playerProfile.apply { this.update() }.textures.skinModel == PlayerTextures.SkinModel.SLIM) + .playerName(player.name) + .ascent(ascent).colorType(colorType).scale(scale).build() +} \ No newline at end of file diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt index a6738f0..7c25e31 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/ChatListener.kt @@ -50,7 +50,7 @@ class ChatListener : Listener { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) fun AsyncChatDecorateEvent.onChatPreview() { - player()?.let { result(result()/*.parseTags(it, false)*/) } + player()?.let { result(result().parseTags(it, false)) } } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt index 3967c40..1df93fb 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/listeners/PlayerListener.kt @@ -6,6 +6,7 @@ import com.mineinabyss.chatty.components.HideJoinLeave import com.mineinabyss.chatty.helpers.* import com.mineinabyss.geary.papermc.tracking.entities.toGeary import com.mineinabyss.idofront.items.editItemMeta +import com.mineinabyss.idofront.textcomponents.miniMsg import com.mineinabyss.idofront.textcomponents.serialize import net.kyori.adventure.text.Component import org.bukkit.entity.Player @@ -24,7 +25,7 @@ class PlayerListener : Listener { fun PlayerJoinEvent.onFirstJoin() { if (player.toGeary().has()) return if (chatty.config.join.enabled && chatty.config.join.firstJoin.enabled) { - joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.firstJoinMessage)) + joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.firstJoinMessage).miniMsg(player.buildTagResolver(true))) } } @@ -33,7 +34,7 @@ class PlayerListener : Listener { val gearyPlayer = player.toGeary() gearyPlayer.getOrSetPersisting { ChannelData() } if (chatty.config.join.enabled && !gearyPlayer.has()) - joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.joinMessage)) + joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.joinMessage).miniMsg(player.buildTagResolver(true))) } @EventHandler @@ -42,7 +43,7 @@ class PlayerListener : Listener { player.refreshSkinInCaches() if (chatty.config.leave.enabled && !player.toGeary().has()) - quitMessage(translatePlaceholders(player, chatty.messages.joinLeave.leaveMessage)) + quitMessage(translatePlaceholders(player, chatty.messages.joinLeave.leaveMessage).miniMsg(player.buildTagResolver(true))) } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -60,7 +61,7 @@ class PlayerListener : Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun SignChangeEvent.onSign() { lines().forEachIndexed { index, line -> - line(index, line.serialize().parseTags(player)) + line(index, line.parseTags(player)) } } diff --git a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/tags/ChattyTags.kt b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/tags/ChattyTags.kt index 39b2230..b345d6b 100644 --- a/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/tags/ChattyTags.kt +++ b/chatty-paper/src/main/kotlin/com/mineinabyss/chatty/tags/ChattyTags.kt @@ -16,6 +16,8 @@ import net.kyori.adventure.text.minimessage.tag.Tag import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver +import net.kyori.adventure.translation.GlobalTranslator +import org.bukkit.Bukkit import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack @@ -39,10 +41,8 @@ object ChattyTags { { component: Component? -> emit(component) } ) - //TODO Figure out why TranslatableComponent here is not properly rendering wihtout converting to plain text (loosing args thus initial component from itemmeta) fun HELD_ITEM(player: Player) = (player.inventory.itemInMainHand.takeUnless { it == ItemStack.empty() } ?: player.inventory.itemInOffHand.takeUnless { it == ItemStack.empty() })?.let { item -> - val component = item.itemMeta?.displayName()?.let { Component.textOfChildren("[".miniMsg(), it, "]".miniMsg()) } - ?: Component.textOfChildren((item.displayName() as TranslatableComponent).let { it.args(it.args().map { it.toPlainText().miniMsg() }) }).toPlainText().miniMsg() + val component = item.itemMeta?.displayName()?.let { Component.textOfChildren("[".miniMsg(), it, "]".miniMsg()) } ?: item.displayName() Placeholder.component("held_item", component.colorIfAbsent(NamedTextColor.AQUA).hoverEvent(item)) }