Many fixes (with the help of claude) #13
No files matched your search
+5
-4
@@ -2,10 +2,11 @@
|
||||
|
||||
NotAlexNoyle 05/06/26:
|
||||
|
||||
- Drop PlaceholderAPI dependency. Resolve Unions-OG union color tags
|
||||
through MiniPlaceholders via Utilities-OG.
|
||||
- Forward more types of exceptions to the server console.
|
||||
- Tighten LuckPerms prefix/suffix spacing and trim handling.
|
||||
- Port all uses of PlaceholderAPI to MiniPlaceholders / Utilities-OG APIs.
|
||||
- Fix Unions-OG Union colors breaking MC -> Discord link.
|
||||
- Drop the per-Discord-role suffix configuration. Player suffixes shown in messages now come exclusively from each player's individual LuckPerms suffix.
|
||||
- Fix LuckPerms spacing so exactly one space appears between the player name and any prefix/suffix.
|
||||
- Configure Color Code Roles.
|
||||
|
||||
NotAlexNoyle 05/04/26:
|
||||
|
||||
|
||||
+3
-1
@@ -51,13 +51,15 @@ repositories {
|
||||
/* ---------------------- Java project deps ---------------------------- */
|
||||
dependencies {
|
||||
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") // Declare Purpur API version to be packaged.
|
||||
compileOnly("net.essentialsx:EssentialsX:2.21.0") // Import EssentialsX API (internally deprecated).
|
||||
compileOnly(
|
||||
files("libs/AnnouncerPlus-1.4.1.jar")
|
||||
) // Import AnnouncerPlus API (binary-compatible with AnnouncerPlus-OG API).
|
||||
compileOnlyApi(project(":libs:Utilities-OG")) // Import TrueOG Network Utilities-OG Java API (from source).
|
||||
compileOnlyApi(project(":libs:Vanish-OG")) // Import TrueOG Network Vanish-OG Java API (from source).
|
||||
compileOnly("net.luckperms:api:5.5") // Import LuckPerms API.
|
||||
compileOnly(
|
||||
"io.github.miniplaceholders:miniplaceholders-api:2.2.3"
|
||||
) // Import MiniPlaceholders API for direct tag access.
|
||||
implementation("tools.jackson.core:jackson-databind:3.1.1")
|
||||
implementation("tools.jackson.dataformat:jackson-dataformat-yaml:3.1.1")
|
||||
|
||||
|
||||
+1
-1
Submodule libs/Utilities-OG updated: cfb722c394...3e26fb1a84.
+1
-1
Submodule libs/Vanish-OG updated: eb4f80f467...3fcdfee824.
@@ -1,6 +1,5 @@
|
||||
package nl.skbotnl.chatog
|
||||
|
||||
import com.earth2me.essentials.Essentials
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
@@ -51,7 +50,6 @@ internal class ChatOG : JavaPlugin() {
|
||||
var translator: OpenAITranslator? = null
|
||||
var discordBridge: DiscordBridge? = null
|
||||
val discordBridgeLock = ReentrantReadWriteLock()
|
||||
var essentials = Bukkit.getServer().pluginManager.getPlugin("Essentials-OG") as Essentials
|
||||
var lastMessagedMap: MutableMap<UUID, UUID> = HashMap()
|
||||
|
||||
fun isConfigInitialized() = ::config.isInitialized
|
||||
|
||||
@@ -19,8 +19,8 @@ import nl.skbotnl.chatog.ChatOG.Companion.discordBridgeLock
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.scope
|
||||
import nl.skbotnl.chatog.util.ChatUtil
|
||||
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
|
||||
import nl.skbotnl.chatog.util.PlayerAffix
|
||||
import nl.skbotnl.chatog.util.PlayerExtensions.chatSystem
|
||||
import nl.skbotnl.chatog.util.PlayerUtils
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
@@ -42,7 +42,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
|
||||
|
||||
scope.launch {
|
||||
discordBridgeLock.read {
|
||||
@@ -66,7 +66,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
|
||||
|
||||
scope.launch {
|
||||
discordBridgeLock.read {
|
||||
@@ -90,7 +90,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
|
||||
|
||||
val reason = PlainTextComponentSerializer.plainText().serialize(event.reason())
|
||||
|
||||
@@ -116,7 +116,7 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player)
|
||||
val playerPartString = ChatUtil.getPlayerPartString(event.player, includeSuffix = true)
|
||||
|
||||
val advancementTitleKey = event.advancement.display?.title() ?: return
|
||||
val advancementTitle = PlainTextComponentSerializer.plainText().serialize(advancementTitleKey)
|
||||
@@ -188,13 +188,12 @@ internal class Events : Listener {
|
||||
return
|
||||
}
|
||||
|
||||
var nameString = "${PlayerAffix.getPrefix(event.player.uniqueId)}${event.player.name}"
|
||||
var nameString = "${PlayerUtils.getPrefix(event.player.uniqueId)}${event.player.name}"
|
||||
|
||||
val unionColorTag =
|
||||
PlainTextComponentSerializer.plainText()
|
||||
.serialize(UtilitiesOG.trueogExpand("<simpleclans_union_color_tag>", event.player))
|
||||
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
|
||||
nameString = "&8[$unionColorTag&8] $nameString"
|
||||
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", event.player)
|
||||
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
|
||||
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
|
||||
nameString = "&8[$unionTag&8] $nameString"
|
||||
}
|
||||
val nameComponent = UtilitiesOG.trueogColorize(legacyToMm(nameString))
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import net.kyori.adventure.text.event.HoverEvent
|
||||
import net.trueog.utilitiesog.UtilitiesOG
|
||||
import nl.skbotnl.chatog.translation.command.TranslateMessage
|
||||
import nl.skbotnl.chatog.util.ChatUtil
|
||||
import nl.skbotnl.chatog.util.PlayerAffix
|
||||
import nl.skbotnl.chatog.util.PlayerUtils
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
internal abstract class ChatSystem {
|
||||
@@ -23,13 +23,17 @@ internal abstract class ChatSystem {
|
||||
var playerPartString = ChatUtil.getPlayerPartString(player)
|
||||
playerPartString = listOfNotNull(prefix, playerPartString).joinToString(" | ")
|
||||
|
||||
sendDiscordMessage(text, playerPartString, player.uniqueId)
|
||||
val discordPlayerPartString =
|
||||
listOfNotNull(prefix, ChatUtil.getPlayerPartString(player, includeSuffix = true)).joinToString(" | ")
|
||||
|
||||
val messageComponent = ChatUtil.processText(text, player) ?: return
|
||||
sendDiscordMessage(text, discordPlayerPartString, player.uniqueId)
|
||||
|
||||
val messageComponent =
|
||||
ChatUtil.processText(text, player)?.color(PlayerUtils.getMessageColor(player.uniqueId)) ?: return
|
||||
|
||||
val chatComponent =
|
||||
UtilitiesOG.trueogColorize(
|
||||
ChatUtil.legacyToMm("$playerPartString<reset>${PlayerAffix.getSuffix(player.uniqueId)} &7> ")
|
||||
ChatUtil.legacyToMm("$playerPartString<reset>${PlayerUtils.getSuffix(player.uniqueId)} &7> ")
|
||||
)
|
||||
|
||||
var textComponent = Component.join(JoinConfiguration.noSeparators(), chatComponent, messageComponent)
|
||||
|
||||
@@ -23,7 +23,4 @@ constructor(
|
||||
@param:JsonProperty("useColorCodeRoles") val useColorCodeRoles: Boolean,
|
||||
@param:JsonProperty("colorCodeRoles") @field:JsonSetter(nulls = Nulls.AS_EMPTY) val colorCodeRoles: List<String>,
|
||||
@param:JsonProperty("roles") @field:JsonSetter(nulls = Nulls.AS_EMPTY) val roles: Map<String, DiscordRoleConfig>,
|
||||
@param:JsonProperty("roleSuffixes")
|
||||
@field:JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||
val roleSuffixes: Map<String, DiscordSuffixConfig>,
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
package nl.skbotnl.chatog.config.discord
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
data class DiscordSuffixConfig @JsonCreator constructor(@param:JsonProperty("suffix") val suffix: String)
|
||||
@@ -187,12 +187,7 @@ internal class DiscordBridge private constructor() {
|
||||
?: NamedTextColor.GRAY
|
||||
}
|
||||
|
||||
val suffix =
|
||||
if (config.discord.roleSuffixes.isEmpty()) {
|
||||
"> "
|
||||
} else {
|
||||
sortedRoles.firstNotNullOfOrNull { r -> config.discord.roleSuffixes[r.id] }?.suffix ?: "> "
|
||||
}
|
||||
val suffix = " > "
|
||||
|
||||
val messageComponents = mutableListOf<Component>(UtilitiesOG.trueogColorize(suffix))
|
||||
|
||||
@@ -368,7 +363,7 @@ internal class DiscordBridge private constructor() {
|
||||
message.contentDisplay,
|
||||
member.effectiveName,
|
||||
Component.join(JoinConfiguration.noSeparators(), discordComponent, userComponent),
|
||||
Component.text("$suffix "),
|
||||
Component.text(suffix),
|
||||
)
|
||||
}
|
||||
return discordBridge
|
||||
@@ -398,7 +393,7 @@ internal class DiscordBridge private constructor() {
|
||||
suspend fun sendMessage(message: String, player: Player) {
|
||||
val webhookMessage =
|
||||
WebhookMessageBuilder()
|
||||
.setUsername(UtilitiesOG.stripFormatting(ChatUtil.getPlayerPartString(player)))
|
||||
.setUsername(UtilitiesOG.stripFormatting(ChatUtil.getPlayerPartString(player, includeSuffix = true)))
|
||||
.setContent(UtilitiesOG.stripFormatting(ChatUtil.stripGroupMentions(ChatUtil.convertMentions(message))))
|
||||
.setAvatarUrl("https://minotar.net/helm/${player.uniqueId}.png")
|
||||
|
||||
|
||||
@@ -4,14 +4,13 @@ import java.util.*
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.JoinConfiguration
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
|
||||
import net.trueog.utilitiesog.UtilitiesOG
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.config
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.languageDatabase
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.translator
|
||||
import nl.skbotnl.chatog.util.ChatUtil
|
||||
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
|
||||
import nl.skbotnl.chatog.util.PlayerAffix
|
||||
import nl.skbotnl.chatog.util.PlayerUtils
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.command.Command
|
||||
import org.bukkit.command.CommandExecutor
|
||||
@@ -138,15 +137,15 @@ internal class TranslateMessage : CommandExecutor {
|
||||
when (messageType) {
|
||||
1 -> {
|
||||
val sentChatMessage = sentMessage as SentChatMessage
|
||||
// TODO: Should use ChatUtil.getPlayerPartString
|
||||
var playerString =
|
||||
"${PlayerAffix.getPrefix(sentChatMessage.player.uniqueId)}${sentChatMessage.player.name}${
|
||||
PlayerAffix.getSuffix(sentChatMessage.player.uniqueId)
|
||||
"${PlayerUtils.getPrefix(sentChatMessage.player.uniqueId)}${sentChatMessage.player.name}${
|
||||
PlayerUtils.getSuffix(sentChatMessage.player.uniqueId)
|
||||
}"
|
||||
val unionColorTag =
|
||||
PlainTextComponentSerializer.plainText()
|
||||
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", sentMessage.player))
|
||||
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
|
||||
playerString = "&8[$unionColorTag&8] $playerString"
|
||||
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player)
|
||||
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
|
||||
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
|
||||
playerString = "&8[$unionTag&8] $playerString"
|
||||
}
|
||||
val playerComponent =
|
||||
UtilitiesOG.trueogColorize(
|
||||
@@ -155,7 +154,6 @@ internal class TranslateMessage : CommandExecutor {
|
||||
)
|
||||
)
|
||||
|
||||
// Don't convert color in translated messages
|
||||
translateMessage =
|
||||
Component.join(
|
||||
JoinConfiguration.noSeparators(),
|
||||
|
||||
@@ -25,14 +25,14 @@ import org.bukkit.Sound
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
internal object ChatUtil {
|
||||
|
|
||||
fun getPlayerPartString(player: Player): String {
|
||||
var playerPart = "${PlayerAffix.getPrefix(player.uniqueId)}${player.name}"
|
||||
fun getPlayerPartString(player: Player, includeSuffix: Boolean = false): String {
|
||||
var playerPart =
|
||||
"${PlayerUtils.getPrefix(player.uniqueId)}${player.name}${if (includeSuffix) PlayerUtils.getSuffix(player.uniqueId) else ""}"
|
||||
|
||||
val unionColorTag =
|
||||
PlainTextComponentSerializer.plainText()
|
||||
.serialize(UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player))
|
||||
if (unionColorTag.isNotEmpty() && unionColorTag != "&8None") {
|
||||
playerPart = legacyToMm("&8[$unionColorTag&8] ") + playerPart
|
||||
val unionTag = UtilitiesOG.trueogExpand("<simpleclans_clan_color_tag>", player)
|
||||
val unionPlainTag = UtilitiesOG.stripFormatting(unionTag)
|
||||
if (unionPlainTag.isNotEmpty() && unionPlainTag != "None") {
|
||||
playerPart = legacyToMm("&8[$unionTag&8] ") + playerPart
|
||||
}
|
||||
|
||||
return playerPart
|
||||
@@ -102,6 +102,10 @@ internal object ChatUtil {
|
||||
return legacyRegex.replace(text) { legacyToMmMap[it.groupValues[1].lowercase()] ?: it.value }
|
||||
}
|
||||
|
||||
fun stripFormatting(text: String): String {
|
||||
return MiniMessage.miniMessage().stripTags(legacyRegex.replace(text, ""))
|
||||
}
|
||||
|
||||
private val getHandle = Regex("@([a-z0-9_.]{2,32})")
|
||||
|
||||
suspend fun convertMentions(text: String): String {
|
||||
@@ -170,8 +174,10 @@ internal object ChatUtil {
|
||||
|
||||
private fun processText(text: String, player: Player?, username: String?, color: Boolean): Component? {
|
||||
val words: MutableList<String> = mutableListOf()
|
||||
val canUseFormatting = player?.hasPermission("chat-og.color") == true || color
|
||||
val textToProcess = if (canUseFormatting) legacyToMm(text) else stripFormatting(text)
|
||||
|
||||
legacyToMm(text).split(" ").forEach { word ->
|
||||
textToProcess.split(" ").forEach { word ->
|
||||
val url = urlRegex.find(word)
|
||||
|
||||
if (url != null) {
|
||||
@@ -206,14 +212,10 @@ internal object ChatUtil {
|
||||
}
|
||||
}
|
||||
|
||||
return if (player?.hasPermission("chat-og.color") == true) {
|
||||
return if (canUseFormatting) {
|
||||
colorMm.deserialize(words.joinToString(" "))
|
||||
} else {
|
||||
if (color) {
|
||||
colorMm.deserialize(words.joinToString(" "))
|
||||
} else {
|
||||
noColorMm.deserialize(words.joinToString(" "))
|
||||
}
|
||||
noColorMm.deserialize(words.joinToString(" "))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package nl.skbotnl.chatog.util
|
||||
|
||||
import java.util.*
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.luckPerms
|
||||
import nl.skbotnl.chatog.util.ChatUtil.legacyToMm
|
||||
|
||||
internal object PlayerAffix {
|
||||
fun getPrefix(uuid: UUID): String {
|
||||
val prefix = getMetaData(uuid).prefix ?: return ""
|
||||
return legacyToMm(prefix).trimEnd() + " "
|
||||
}
|
||||
|
||||
fun getSuffix(uuid: UUID): String {
|
||||
val suffix = getMetaData(uuid).suffix ?: return ""
|
||||
return " " + legacyToMm(suffix).trim()
|
||||
}
|
||||
|
||||
private fun getMetaData(uuid: UUID) =
|
||||
with(luckPerms.userManager.getUser(uuid) ?: luckPerms.userManager.loadUser(uuid).join()) {
|
||||
val queryOptions = luckPerms.contextManager.getQueryOptions(this).orElse(queryOptions)
|
||||
cachedData.getMetaData(queryOptions)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package nl.skbotnl.chatog.util
|
||||
|
||||
import java.util.*
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import net.kyori.adventure.text.format.TextColor
|
||||
import nl.skbotnl.chatog.ChatOG.Companion.luckPerms
|
||||
|
||||
internal object PlayerUtils {
|
||||
fun getPrefix(uuid: UUID): String {
|
||||
val prefix = getMetaData(uuid).prefix?.trim() ?: return ""
|
||||
return if (prefix.endsWith(' ')) prefix else "$prefix "
|
||||
}
|
||||
|
||||
fun getSuffix(uuid: UUID) = getMetaData(uuid).suffix?.trim() ?: ""
|
||||
|
||||
fun getMessageColor(uuid: UUID): TextColor {
|
||||
return getMetaData(uuid).getMetaValue("chat-og.messageColor")?.lowercase()?.let {
|
||||
NamedTextColor.NAMES.value(it)
|
||||
} ?: NamedTextColor.GRAY
|
||||
}
|
||||
|
||||
private fun getMetaData(uuid: UUID) =
|
||||
with(luckPerms.userManager.getUser(uuid) ?: luckPerms.userManager.loadUser(uuid).join()) { cachedData.metaData }
|
||||
}
|
||||
@@ -64,31 +64,53 @@ discord:
|
||||
listCommandText: "There are %onlineplayers% out of maximum 200 players online."
|
||||
|
||||
# Color code roles
|
||||
# TODO: Configure these roles.
|
||||
useColorCodeRoles: false
|
||||
# Configure which roles can use color codes with Discord -> Minecraft
|
||||
# For example:
|
||||
#
|
||||
# colorCodeRoles: [1234567891274567890, 0987654321983654321]
|
||||
# When true, only Discord users with one of the colorCodeRoles below can use
|
||||
# Utilities-OG ColorCodeMap Bukkit codes (&c, &l, &*) or MiniMessage tags
|
||||
# in Discord -> Minecraft chat. All others have their input stripped of
|
||||
# formatting before it is rendered in-game.
|
||||
useColorCodeRoles: true
|
||||
|
||||
# Configure the text color (after the username (Discord -> Minecraft)) for certain roles
|
||||
# For example:
|
||||
#
|
||||
# roles:
|
||||
# 1234567891239567890:
|
||||
# messageColor: red
|
||||
#
|
||||
# message_color can be one of the following: black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray, dark_gray, blue, green, aqua, red, light_purple, yellow, white
|
||||
#
|
||||
# You can also use RGB for the color:
|
||||
#
|
||||
# roles:
|
||||
# 1234567891234567890:
|
||||
# messageColor: [255, 255, 255]
|
||||
# Discord role IDs allowed to use color codes from Discord.
|
||||
colorCodeRoles:
|
||||
- "897300356395073546" # OG-Pro
|
||||
- "934876475285467247" # OG Master
|
||||
- "897299946926125167" # Tester (same level as OG Master)
|
||||
- "897299018407890945" # YouTube
|
||||
- "1391741516204081162" # Technical Contributor
|
||||
- "1096297706034569287" # Helper
|
||||
- "897776965849858101" # Builder
|
||||
- "916661431850639420" # MVP
|
||||
- "1080912883694719047" # Developer
|
||||
- "897297817574440970" # Moderator
|
||||
- "897296878490763344" # Admin
|
||||
|
||||
# Configure the suffix for roles (has to include the space). color codes are allowed.
|
||||
# For example:
|
||||
|
||||
# roleSuffixes:
|
||||
# 1234567891234527890:
|
||||
# suffix: ": "
|
||||
# Default Minecraft chat color applied to a Discord -> Minecraft message when
|
||||
# the sender did not type their own color code. If a user has multiple
|
||||
# configured roles the entry for their highest-positioned role wins.
|
||||
roles:
|
||||
"897657230575431710": # Member
|
||||
messageColor: gray
|
||||
"897300768619659314": # OG
|
||||
messageColor: white
|
||||
"897300356395073546": # OG-Pro
|
||||
messageColor: white
|
||||
"934876475285467247": # OG Master
|
||||
messageColor: white
|
||||
"897299946926125167": # Tester
|
||||
messageColor: white
|
||||
"897299018407890945": # YouTube
|
||||
messageColor: white
|
||||
"1391741516204081162": # Technical Contributor
|
||||
messageColor: white
|
||||
"1096297706034569287": # Helper
|
||||
messageColor: white
|
||||
"897776965849858101": # Builder
|
||||
messageColor: white
|
||||
"916661431850639420": # MVP
|
||||
messageColor: white
|
||||
"1080912883694719047": # Developer
|
||||
messageColor: white
|
||||
"897297817574440970": # Moderator
|
||||
messageColor: white
|
||||
"897296878490763344": # Admin
|
||||
messageColor: white
|
||||
@@ -7,8 +7,7 @@ depend:
|
||||
- LuckPerms
|
||||
- AnnouncerPlus
|
||||
- Utilities-OG
|
||||
softdepend:
|
||||
- Essentials-OG
|
||||
- Vanish-OG
|
||||
commands:
|
||||
translatemessage:
|
||||
description: Internal command for translating messages
|
||||
|
||||
Reference in new issue
Block a user
This looks very overengineered
Its like that because it has to detect if union placeholder is "None" (including color codes) so union name is hidden along with its surrounding brackets. It can be simplified if those things can be hidden in a more elegant way when the player has no union.