Skip to content

Commit

Permalink
refactor: restructure codebase, serialize stuff as little as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Jan 12, 2024
1 parent df63f6c commit 30ea9e5
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String>.removeFirstArgumentOfStringList(): String =
this.filter { it != this.first() }.toSentence()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +14,6 @@ data class ChattyNickname(val nickname: String)
var Player.chattyNickname
get() = this.toGeary().get<ChattyNickname>()?.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<ChattyNickname>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> =
chatty.config.ping.let { ping -> if ("*" in ping.alternativePingSounds || "all" in ping.alternativePingSounds)
Sound.entries.map { it.key.toString() }.toList() else ping.alternativePingSounds }
Expand All @@ -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()
Expand All @@ -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("<chatty_nickname>: hi", NamedTextColor.RED), being serialized to "<red>\<chatty_nickname>: 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())

Expand All @@ -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<OfflinePlayer, Component>()
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<OfflinePlayer, Component>()
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 "<font:$font>${ImageUtils.generateStringFromImage(image, colorType, ascent)}</font>".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 "<font:$font>${ImageUtils.generateStringFromImage(image, colorType, ascent)}</font>".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)(?!\")(?!:)", "")

Expand All @@ -184,19 +118,16 @@ fun List<String>.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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<OfflinePlayer, Component>()
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<OfflinePlayer, Component>()
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 "<font:$font>${ImageUtils.generateStringFromImage(image, colorType, ascent)}</font>".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 "<font:$font>${ImageUtils.generateStringFromImage(image, colorType, ascent)}</font>".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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,7 +25,7 @@ class PlayerListener : Listener {
fun PlayerJoinEvent.onFirstJoin() {
if (player.toGeary().has<ChannelData>()) 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)))
}
}

Expand All @@ -33,7 +34,7 @@ class PlayerListener : Listener {
val gearyPlayer = player.toGeary()
gearyPlayer.getOrSetPersisting { ChannelData() }
if (chatty.config.join.enabled && !gearyPlayer.has<HideJoinLeave>())
joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.joinMessage))
joinMessage(translatePlaceholders(player, chatty.messages.joinLeave.joinMessage).miniMsg(player.buildTagResolver(true)))
}

@EventHandler
Expand All @@ -42,7 +43,7 @@ class PlayerListener : Listener {
player.refreshSkinInCaches()

if (chatty.config.leave.enabled && !player.toGeary().has<HideJoinLeave>())
quitMessage(translatePlaceholders(player, chatty.messages.joinLeave.leaveMessage))
quitMessage(translatePlaceholders(player, chatty.messages.joinLeave.leaveMessage).miniMsg(player.buildTagResolver(true)))
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
Expand All @@ -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))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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))
}

Expand Down

0 comments on commit 30ea9e5

Please sign in to comment.