From 3445b792c82f47bd449172e11282d58b19ae6b3d Mon Sep 17 00:00:00 2001 From: Ceikry Date: Wed, 1 Mar 2023 00:02:12 +0000 Subject: [PATCH] Implemented Global Chat Added global chat feature, so players can still maintain global communication while being in their own clan chat An individual player can opt out of global chat by using the ::muteglobal command Implemented automatic message splitting for clan and global chat Implemented colour selection for global chat (prefix your message with hex colour code, e.g. //%690420 before the message) --- Server/src/main/core/ServerConstants.kt | 3 ++ .../game/system/communication/GlobalChat.kt | 40 ++++++++++++++ .../game/system/config/ServerConfigParser.kt | 1 + .../main/core/net/packet/PacketProcessor.kt | 52 ++++++++++++++++--- Server/src/main/core/tools/Globals.kt | 20 ++++--- 5 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 Server/src/main/core/game/system/communication/GlobalChat.kt diff --git a/Server/src/main/core/ServerConstants.kt b/Server/src/main/core/ServerConstants.kt index 5823be465..d060ff29a 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -264,5 +264,8 @@ class ServerConstants { @JvmField var DRAGON_AXE_USE_OSRS_SPEC = false + + @JvmField + var ENABLE_GLOBALCHAT = false } } diff --git a/Server/src/main/core/game/system/communication/GlobalChat.kt b/Server/src/main/core/game/system/communication/GlobalChat.kt new file mode 100644 index 000000000..98e60229a --- /dev/null +++ b/Server/src/main/core/game/system/communication/GlobalChat.kt @@ -0,0 +1,40 @@ +package core.game.system.communication + +import core.api.Commands +import core.api.getAttribute +import core.api.sendMessage +import core.api.setAttribute +import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.tools.colorize + +class GlobalChat : Commands { + override fun defineCommands() { + define("muteglobal", Privilege.STANDARD, "", "Toggles global chat on or off.") {player, _ -> + val original = getAttribute(player, ATTR_GLOBAL_MUTE, false) + setAttribute(player, ATTR_GLOBAL_MUTE, !original) + sendMessage(player, "Global chat is now ${if (original) "ON" else "OFF"}.") + return@define + } + } + + companion object { + val ATTR_GLOBAL_MUTE = "/save:globalmute" + fun process(sender: String, message: String) { + val msgSD = prepare(sender, message, false) + val msgHD = prepare(sender, message, true) + for (player in Repository.players.filter { !getAttribute(it, ATTR_GLOBAL_MUTE, false) }) { + if (player.interfaceManager.isResizable) + sendMessage(player, msgHD) + else + sendMessage(player, msgSD) + } + } + + private fun prepare(sender: String, message: String, isResizable: Boolean): String { + val baseColor = if (isResizable) "%G" else "%7512ff" + val bracketColor = if (isResizable) "%ffffff" else "%000000" + return colorize("$bracketColor[${baseColor}G$bracketColor] $sender: ${baseColor}$message") + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index a486db759..413e55cf7 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -133,6 +133,7 @@ object ServerConfigParser { ServerConstants.DISCORD_MOD_WEBHOOK = data.getString("server.moderation_webhook", "") ServerConstants.NOAUTH_DEFAULT_ADMIN = data.getBoolean("server.noauth_default_admin", false) ServerConstants.DRAGON_AXE_USE_OSRS_SPEC = data.getBoolean("world.dragon_axe_use_osrs_spec", false) + ServerConstants.ENABLE_GLOBALCHAT = data.getBoolean("world.enable_globalchat", true) } diff --git a/Server/src/main/core/net/packet/PacketProcessor.kt b/Server/src/main/core/net/packet/PacketProcessor.kt index 59d67c9b3..7fb86cc21 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -47,6 +47,7 @@ import core.game.node.entity.player.info.LogType import core.game.node.entity.player.info.PlayerMonitor import core.tools.SystemLogger import core.game.system.command.CommandSystem +import core.game.system.communication.GlobalChat import core.game.world.GameWorld import core.game.world.repository.Repository import core.net.packet.`in`.Packet @@ -54,6 +55,7 @@ import core.net.packet.`in`.RunScript import core.worker.ManagementEvents import java.io.PrintWriter import java.io.StringWriter +import java.lang.Math.min import java.util.* object PacketProcessor { @@ -198,13 +200,28 @@ object PacketProcessor { if (pkt.player.details.isMuted) pkt.player.sendMessage("You have been muted due to breaking a rule.") else { - if (pkt.message.startsWith("/") && pkt.player.communication.clan != null) { - val builder = ClanMessage.newBuilder() - builder.sender = pkt.player.name - builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_") - builder.message = pkt.message.substring(1) - builder.rank = pkt.player.rights.ordinal - ManagementEvents.publish(builder.build()) + if (ServerConstants.ENABLE_GLOBALCHAT && pkt.message.startsWith("//")) { + if (getAttribute(pkt.player, GlobalChat.ATTR_GLOBAL_MUTE, false)) + return + + val messages = splitChatMessage(pkt.message.substring(2), pkt.player.name.length + 3, false) + for (message in messages) { + if (message.isNotBlank()) + GlobalChat.process(pkt.player.username, message) + } + return + } + else if (pkt.message.startsWith("/") && pkt.player.communication.clan != null) { + val messages = splitChatMessage(pkt.message.substring(1), pkt.player.communication.clan.name.length + pkt.player.name.length, pkt.player.details.rights.ordinal != 0) + for (message in messages) { + if (message.isBlank()) continue + val builder = ClanMessage.newBuilder() + builder.sender = pkt.player.name + builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_") + builder.message = message + builder.rank = pkt.player.rights.ordinal + ManagementEvents.publish(builder.build()) + } return } PlayerMonitor.logChat(pkt.player, "public", pkt.message) @@ -759,5 +776,24 @@ object PacketProcessor { container.insert(slot, secondSlot, false) } container.refresh() - } + } + + fun splitChatMessage(message: String, clanLength: Int, rankPresent: Boolean) : ArrayList { + val messages = ArrayList() + + val effectiveCutoff = BASE_CHAT_CUTOFF - (clanLength + if (rankPresent) 9 else 0) + var counter = 0 + for (token in message.split(" ")) { + if (counter + token.length > effectiveCutoff) + break + counter += token.length + 1 + } + messages.add(message.substring(0, min(counter, message.length))) + if (counter < message.length) + messages.add(message.substring(counter, message.length)) + + return messages + } + + const val BASE_CHAT_CUTOFF = 81 } \ No newline at end of file diff --git a/Server/src/main/core/tools/Globals.kt b/Server/src/main/core/tools/Globals.kt index ed2f85f40..cb070f762 100644 --- a/Server/src/main/core/tools/Globals.kt +++ b/Server/src/main/core/tools/Globals.kt @@ -7,14 +7,18 @@ const val GREEN = "" const val BLUE = "" const val PURPLE = "" -fun colorize(line: String): String{ - val new = line.replace("%R", RED) - .replace("%O", ORANGE) - .replace("%Y", YELLOW) - .replace("%G", GREEN) - .replace("%B", BLUE) - .replace("%P", PURPLE).append("") + " " - return new +private val pattern = Regex("%[0-9a-fA-F]{6}") +private val testData = arrayOf("This is a string with no colors.", "This %R is a string with one color.", "This %R %G %B is a string with multiple colors.", "This %ffffff is an arbitrary hex string.") + +fun colorize(line: String): String { + return line.replace("%R", RED) + .replace("%O", ORANGE) + .replace("%Y", YELLOW) + .replace("%G", GREEN) + .replace("%B", BLUE) + .replace("%P", PURPLE) + .replace(pattern) { matchResult -> "" } + .append("") + " " } fun colorize(line: String, hexColor: String): String{