diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..a7aeec6f2 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,6 @@ +FROM eclipse-temurin:17-jdk-jammy + +USER root + +# set working directory +WORKDIR /workspaces/RPKit \ No newline at end of file diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000..520baaf3c --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,33 @@ +# Dev Container +This directory contains files related to the dev container for the project, which is a defined environment with the dependencies for compilation pre-installed. + +## Install Docker +If you do not already have Docker installed, download it [here](https://www.docker.com/get-started/)_. + +## Entering Dev Container Environment via VSCode +To enter the dev container environment using VSCode, follow these steps: +1. Click on the "Open a Remote Window" button in the bottom left of VSCode +1. Click "Reopen in Container" + +## Entering Dev Container Environment via Command Line +### Windows +To enter the dev container environment via the command line on Windows, run the following commands: +``` +cd .\.devcontainer +.\start_dev_container.bat +``` + +### Linux +To enter the dev container environment via the command line on Linux, run the following commands: +``` +cd ./.devcontainer +./start_dev_container.sh +``` + +## Compiling +To compile the project in the provided dev container, follow these steps: +1. Make sure the End of Line (EOF) Sequence for the `gradlew` script is set to LF. If it is set to CRLF, the script will fail to run. +1. Run the following command: +``` +./gradlew clean build +``` \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..99b37e85c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,32 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu +{ + "name": "rpkdevcontainer", + "build": { + // Path is relative to the devcontainer.json file. + "dockerfile": "Dockerfile" + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "vscjava.vscode-java-pack", + "vscjava.vscode-gradle", + "fwcd.kotlin" + ] + } + }, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + "remoteUser": "root" +} diff --git a/.devcontainer/start_dev_container.bat b/.devcontainer/start_dev_container.bat new file mode 100644 index 000000000..ffac4fd0a --- /dev/null +++ b/.devcontainer/start_dev_container.bat @@ -0,0 +1,4 @@ +REM This script is meant to be run from the .devcontainer directory. + +docker build . -t rpk-dev-container +docker run -it -v %cd%\..\:/workspaces/RPKit rpk-dev-container /bin/bash \ No newline at end of file diff --git a/.devcontainer/start_dev_container.sh b/.devcontainer/start_dev_container.sh new file mode 100644 index 000000000..7f482714d --- /dev/null +++ b/.devcontainer/start_dev_container.sh @@ -0,0 +1,4 @@ +# This script is meant to be run from the .devcontainer directory. + +docker build . -t rpk-dev-container +docker run -it -v %cd%\..\:/workspaces/RPKit rpk-dev-container /bin/bash \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/RPKChatBukkit.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/RPKChatBukkit.kt index 296a28c13..b5bbb52aa 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/RPKChatBukkit.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/RPKChatBukkit.kt @@ -37,6 +37,7 @@ import com.rpkit.chat.bukkit.command.listchatchannels.ListChatChannelsCommand import com.rpkit.chat.bukkit.command.message.MessageCommand import com.rpkit.chat.bukkit.command.mute.MuteCommand import com.rpkit.chat.bukkit.command.reply.ReplyCommand +import com.rpkit.chat.bukkit.command.setchatnamecolor.SetChatNameColorCommand import com.rpkit.chat.bukkit.command.snoop.SnoopCommand import com.rpkit.chat.bukkit.command.unmute.UnmuteCommand import com.rpkit.chat.bukkit.database.table.* @@ -55,6 +56,8 @@ import com.rpkit.chat.bukkit.prefix.RPKPrefixServiceImpl import com.rpkit.chat.bukkit.snooper.RPKSnooperService import com.rpkit.chat.bukkit.snooper.RPKSnooperServiceImpl import com.rpkit.chat.bukkit.speaker.RPKChatChannelSpeakerService +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorService +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorServiceImpl import com.rpkit.core.bukkit.command.toBukkit import com.rpkit.core.bukkit.listener.registerListeners import com.rpkit.core.database.Database @@ -182,6 +185,7 @@ class RPKChatBukkit : JavaPlugin(), RPKPlugin { database.addTable(RPKChatGroupMemberTable(database, this)) database.addTable(RPKLastUsedChatGroupTable(database, this)) database.addTable(RPKSnooperTable(database, this)) + database.addTable(RPKChatNameColorTable(database, this)) // Class loader needs to be the plugin's class loader in order for ServiceLoader in slf4j to be able to find // the SLF4JLoggerProvider implementation, as the remapped slf4j-jdk14 is loaded by the plugin's class loader @@ -207,6 +211,7 @@ class RPKChatBukkit : JavaPlugin(), RPKPlugin { Services[RPKChatChannelSpeakerService::class.java] = RPKChatChannelSpeakerService(this) Services[RPKChatGroupService::class.java] = RPKChatGroupServiceImpl(this) Services[RPKSnooperService::class.java] = RPKSnooperServiceImpl(this) + Services[RPKChatNameColorService::class.java] = RPKChatNameColorServiceImpl(this) registerChatChannelPermissions(chatChannelService) registerPrefixPermissions(prefixService) @@ -233,6 +238,7 @@ class RPKChatBukkit : JavaPlugin(), RPKPlugin { getCommand("message")?.setExecutor(MessageCommand(this)) getCommand("reply")?.setExecutor(ReplyCommand(this)) getCommand("snoop")?.setExecutor(SnoopCommand(this)) + getCommand("setchatnamecolor")?.setExecutor(SetChatNameColorCommand(this)) Services[RPKChatChannelService::class.java]?.chatChannels?.forEach { chatChannel -> getDynamicCommand(chatChannel.name.value).setExecutor(QuickChatChannelCommand(this, chatChannel).toBukkit()) } diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/RPKChatChannelImpl.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/RPKChatChannelImpl.kt index 0023580fc..98169a437 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/RPKChatChannelImpl.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/RPKChatChannelImpl.kt @@ -21,6 +21,7 @@ import com.rpkit.chat.bukkit.chatchannel.context.DirectedPostFormatMessageContex import com.rpkit.chat.bukkit.chatchannel.context.DirectedPreFormatMessageContextImpl import com.rpkit.chat.bukkit.chatchannel.context.UndirectedMessageContextImpl import com.rpkit.chat.bukkit.chatchannel.format.FormatPart +import com.rpkit.chat.bukkit.chatchannel.format.part.SenderCharacterNamePart import com.rpkit.chat.bukkit.chatchannel.pipeline.DirectedPostFormatPipelineComponent import com.rpkit.chat.bukkit.chatchannel.pipeline.DirectedPreFormatPipelineComponent import com.rpkit.chat.bukkit.chatchannel.pipeline.UndirectedPipelineComponent @@ -189,5 +190,4 @@ class RPKChatChannelImpl( } }) } - } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverCharacterNamePart.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverCharacterNamePart.kt index 28d59de06..f7ace1fd8 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverCharacterNamePart.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverCharacterNamePart.kt @@ -21,12 +21,16 @@ import com.rpkit.chat.bukkit.RPKChatBukkit import com.rpkit.chat.bukkit.chatchannel.format.click.ClickAction import com.rpkit.chat.bukkit.chatchannel.format.hover.HoverAction import com.rpkit.chat.bukkit.context.DirectedPreFormatMessageContext -import com.rpkit.core.service.Services +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.TextComponent import org.bukkit.Bukkit import org.bukkit.configuration.serialization.ConfigurationSerializable import org.bukkit.configuration.serialization.SerializableAs import java.util.concurrent.CompletableFuture.supplyAsync import java.util.logging.Level +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorService +import com.rpkit.core.service.Services @SerializableAs("ReceiverCharacterNamePart") class ReceiverCharacterNamePart( @@ -94,4 +98,52 @@ class ReceiverCharacterNamePart( serialized["click"] as? ClickAction ) } + + override fun toChatComponents(context: DirectedPreFormatMessageContext) = supplyAsync { + TextComponent.fromLegacyText(getText(context).join()).also { + for (component in it) { + + if (font != null) component.font = font + + if (plugin.config.getBoolean("misc.characterChatNameColorOverrideEnabled")) { + component.color = getChatNameColor(context) + } + else { + if (color != null) component.color = ChatColor.of(color) + } + + if (isBold != null) component.isBold = isBold + if (isItalic != null) component.isItalic = isItalic + if (isUnderlined != null) component.isUnderlined = isUnderlined + if (isStrikethrough != null) component.isStrikethrough = isStrikethrough + if (isObfuscated != null) component.isObfuscated = isObfuscated + if (insertion != null) component.insertion = insertion + if (hover != null) component.hoverEvent = hover.toHoverEvent(context).join() + if (click != null) component.clickEvent = click.toClickEvent(context).join() + } + } + }.exceptionally { exception -> + plugin.logger.log(Level.SEVERE, "Failed to convert text part to chat components", exception) + throw exception + } + + private fun getChatNameColor(context: DirectedPreFormatMessageContext): ChatColor { + val chatNameColorService = Services[RPKChatNameColorService::class.java] + val senderMinecraftProfile = context.senderMinecraftProfile + if (senderMinecraftProfile != null) { + val overriddenChatNameColor = chatNameColorService?.getChatNameColor(senderMinecraftProfile) + if (overriddenChatNameColor != null) { + return ChatColor.of(overriddenChatNameColor) + } + } + return getConfiguredChatPartColorOrDefault() + } + + private fun getConfiguredChatPartColorOrDefault(): ChatColor { + return if (color != null) { + ChatColor.of(color) + } else { + ChatColor.WHITE + } + } } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverProfileNamePart.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverProfileNamePart.kt index e9fd9b61a..ed526b132 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverProfileNamePart.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/ReceiverProfileNamePart.kt @@ -24,10 +24,17 @@ import org.bukkit.Bukkit import org.bukkit.configuration.serialization.ConfigurationSerializable import org.bukkit.configuration.serialization.SerializableAs import java.util.concurrent.CompletableFuture.completedFuture +import net.md_5.bungee.api.chat.TextComponent +import java.util.concurrent.CompletableFuture.supplyAsync +import net.md_5.bungee.api.ChatColor +import java.util.logging.Level +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorService +import com.rpkit.core.service.Services @SerializableAs("ReceiverProfileNamePart") class ReceiverProfileNamePart( - plugin: RPKChatBukkit, + private val plugin: RPKChatBukkit, font: String? = null, color: String? = null, isBold: Boolean? = null, @@ -84,4 +91,52 @@ class ReceiverProfileNamePart( serialized["click"] as? ClickAction ) } + + override fun toChatComponents(context: DirectedPreFormatMessageContext) = supplyAsync { + TextComponent.fromLegacyText(getText(context).join()).also { + for (component in it) { + + if (font != null) component.font = font + + if (plugin.config.getBoolean("misc.profileChatNameColorOverrideEnabled")) { + component.color = getChatNameColor(context) + } + else { + if (color != null) component.color = ChatColor.of(color) + } + + if (isBold != null) component.isBold = isBold + if (isItalic != null) component.isItalic = isItalic + if (isUnderlined != null) component.isUnderlined = isUnderlined + if (isStrikethrough != null) component.isStrikethrough = isStrikethrough + if (isObfuscated != null) component.isObfuscated = isObfuscated + if (insertion != null) component.insertion = insertion + if (hover != null) component.hoverEvent = hover.toHoverEvent(context).join() + if (click != null) component.clickEvent = click.toClickEvent(context).join() + } + } + }.exceptionally { exception -> + plugin.logger.log(Level.SEVERE, "Failed to convert text part to chat components", exception) + throw exception + } + + private fun getChatNameColor(context: DirectedPreFormatMessageContext): ChatColor { + val chatNameColorService = Services[RPKChatNameColorService::class.java] + val senderMinecraftProfile = context.senderMinecraftProfile + if (senderMinecraftProfile != null) { + val overriddenChatNameColor = chatNameColorService?.getChatNameColor(senderMinecraftProfile) + if (overriddenChatNameColor != null) { + return ChatColor.of(overriddenChatNameColor) + } + } + return getConfiguredChatPartColorOrDefault() + } + + private fun getConfiguredChatPartColorOrDefault(): ChatColor { + return if (color != null) { + ChatColor.of(color) + } else { + ChatColor.WHITE + } + } } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderCharacterNamePart.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderCharacterNamePart.kt index 228bcec85..66664afdb 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderCharacterNamePart.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderCharacterNamePart.kt @@ -21,12 +21,16 @@ import com.rpkit.chat.bukkit.RPKChatBukkit import com.rpkit.chat.bukkit.chatchannel.format.click.ClickAction import com.rpkit.chat.bukkit.chatchannel.format.hover.HoverAction import com.rpkit.chat.bukkit.context.DirectedPreFormatMessageContext -import com.rpkit.core.service.Services +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.TextComponent import org.bukkit.Bukkit import org.bukkit.configuration.serialization.ConfigurationSerializable import org.bukkit.configuration.serialization.SerializableAs import java.util.concurrent.CompletableFuture.supplyAsync import java.util.logging.Level +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorService +import com.rpkit.core.service.Services @SerializableAs("SenderCharacterNamePart") class SenderCharacterNamePart( @@ -98,4 +102,52 @@ class SenderCharacterNamePart( serialized["click"] as? ClickAction ) } + + override fun toChatComponents(context: DirectedPreFormatMessageContext) = supplyAsync { + TextComponent.fromLegacyText(getText(context).join()).also { + for (component in it) { + + if (font != null) component.font = font + + if (plugin.config.getBoolean("misc.characterChatNameColorOverrideEnabled")) { + component.color = getChatNameColor(context) + } + else { + if (color != null) component.color = ChatColor.of(color) + } + + if (isBold != null) component.isBold = isBold + if (isItalic != null) component.isItalic = isItalic + if (isUnderlined != null) component.isUnderlined = isUnderlined + if (isStrikethrough != null) component.isStrikethrough = isStrikethrough + if (isObfuscated != null) component.isObfuscated = isObfuscated + if (insertion != null) component.insertion = insertion + if (hover != null) component.hoverEvent = hover.toHoverEvent(context).join() + if (click != null) component.clickEvent = click.toClickEvent(context).join() + } + } + }.exceptionally { exception -> + plugin.logger.log(Level.SEVERE, "Failed to convert text part to chat components", exception) + throw exception + } + + private fun getChatNameColor(context: DirectedPreFormatMessageContext): ChatColor { + val chatNameColorService = Services[RPKChatNameColorService::class.java] + val senderMinecraftProfile = context.senderMinecraftProfile + if (senderMinecraftProfile != null) { + val overriddenChatNameColor = chatNameColorService?.getChatNameColor(senderMinecraftProfile) + if (overriddenChatNameColor != null) { + return ChatColor.of(overriddenChatNameColor) + } + } + return getConfiguredChatPartColorOrDefault() + } + + private fun getConfiguredChatPartColorOrDefault(): ChatColor { + return if (color != null) { + ChatColor.of(color) + } else { + ChatColor.WHITE + } + } } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderProfileNamePart.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderProfileNamePart.kt index e54817720..4cb3ebbfc 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderProfileNamePart.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/SenderProfileNamePart.kt @@ -24,10 +24,17 @@ import org.bukkit.Bukkit import org.bukkit.configuration.serialization.ConfigurationSerializable import org.bukkit.configuration.serialization.SerializableAs import java.util.concurrent.CompletableFuture.completedFuture +import net.md_5.bungee.api.chat.TextComponent +import java.util.concurrent.CompletableFuture.supplyAsync +import net.md_5.bungee.api.ChatColor +import java.util.logging.Level +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable +import com.rpkit.chat.bukkit.chatnamecolor.RPKChatNameColorService +import com.rpkit.core.service.Services @SerializableAs("SenderProfileNamePart") class SenderProfileNamePart( - plugin: RPKChatBukkit, + private val plugin: RPKChatBukkit, font: String? = null, color: String? = null, isBold: Boolean? = null, @@ -83,4 +90,52 @@ class SenderProfileNamePart( serialized["click"] as? ClickAction ) } + + override fun toChatComponents(context: DirectedPreFormatMessageContext) = supplyAsync { + TextComponent.fromLegacyText(getText(context).join()).also { + for (component in it) { + + if (font != null) component.font = font + + if (plugin.config.getBoolean("misc.profileChatNameColorOverrideEnabled")) { + component.color = getChatNameColor(context) + } + else { + if (color != null) component.color = ChatColor.of(color) + } + + if (isBold != null) component.isBold = isBold + if (isItalic != null) component.isItalic = isItalic + if (isUnderlined != null) component.isUnderlined = isUnderlined + if (isStrikethrough != null) component.isStrikethrough = isStrikethrough + if (isObfuscated != null) component.isObfuscated = isObfuscated + if (insertion != null) component.insertion = insertion + if (hover != null) component.hoverEvent = hover.toHoverEvent(context).join() + if (click != null) component.clickEvent = click.toClickEvent(context).join() + } + } + }.exceptionally { exception -> + plugin.logger.log(Level.SEVERE, "Failed to convert text part to chat components", exception) + throw exception + } + + private fun getChatNameColor(context: DirectedPreFormatMessageContext): ChatColor { + val chatNameColorService = Services[RPKChatNameColorService::class.java] + val senderMinecraftProfile = context.senderMinecraftProfile + if (senderMinecraftProfile != null) { + val overriddenChatNameColor = chatNameColorService?.getChatNameColor(senderMinecraftProfile) + if (overriddenChatNameColor != null) { + return ChatColor.of(overriddenChatNameColor) + } + } + return getConfiguredChatPartColorOrDefault() + } + + private fun getConfiguredChatPartColorOrDefault(): ChatColor { + return if (color != null) { + ChatColor.of(color) + } else { + ChatColor.WHITE + } + } } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorServiceImpl.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorServiceImpl.kt new file mode 100644 index 000000000..1c02ad287 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorServiceImpl.kt @@ -0,0 +1,22 @@ +package com.rpkit.chat.bukkit.chatnamecolor + +import com.rpkit.chat.bukkit.RPKChatBukkit +import com.rpkit.players.bukkit.profile.minecraft.RPKMinecraftProfile +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable + +/** + * Chat name color service implementation + */ +class RPKChatNameColorServiceImpl(override val plugin: RPKChatBukkit) : RPKChatNameColorService { + + override fun getChatNameColor(minecraftProfile: RPKMinecraftProfile?): String? { + val minecraftProfileId = minecraftProfile?.id + if (minecraftProfileId != null) { + val chatNameColorRecord = plugin.database.getTable(RPKChatNameColorTable::class.java)[minecraftProfileId].join() + if (chatNameColorRecord != null) { + return chatNameColorRecord.chatNameColor + } + } + return null + } +} \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/command/setchatnamecolor/SetChatNameColorCommand.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/command/setchatnamecolor/SetChatNameColorCommand.kt new file mode 100644 index 000000000..5fe07329e --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/command/setchatnamecolor/SetChatNameColorCommand.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2020 Ren Binden + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rpkit.chat.bukkit.command.setchatnamecolor + +import com.rpkit.chat.bukkit.RPKChatBukkit +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable +import com.rpkit.core.service.Services +import com.rpkit.players.bukkit.profile.minecraft.RPKMinecraftProfileId +import com.rpkit.players.bukkit.profile.minecraft.RPKMinecraftProfileService +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletableFuture.runAsync +import java.util.logging.Level.SEVERE + +/** + * SetChatNameColor command. + * Sets the chat color name for a player. + */ +class SetChatNameColorCommand(private val plugin: RPKChatBukkit) : CommandExecutor { + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + if (!sender.hasPermission("rpkit.chat.command.setchatnamecolor")) { + sender.sendMessage(plugin.messages.noPermissionSetchatnamecolor) + return true + } + if (sender !is Player) { + sender.sendMessage(plugin.messages.notFromConsole) + return true + } + val minecraftProfileService = Services[RPKMinecraftProfileService::class.java] + if (minecraftProfileService == null) { + sender.sendMessage(plugin.messages.noMinecraftProfileService) + return true + } + val minecraftProfile = minecraftProfileService.getPreloadedMinecraftProfile(sender) + if (minecraftProfile == null) { + sender.sendMessage(plugin.messages.noMinecraftProfile) + return true + } + + // if no arguments + if (args.isEmpty()) { + sender.sendMessage(plugin.messages.setchatnamecolorUsage) + return true + } + + // retrieve desired chat name color + val chatNameColor = args[0] + if (!isHexColorCodeValid(chatNameColor)) { + sender.sendMessage(plugin.messages.setchatnamecolorInvalidColorCode) + return true + } + + // if a second argument is provided + if (args.size > 1) { + // check that sender has permission to set the chat name colors of other players + if (!sender.hasPermission("rpkit.chat.command.setchatnamecolor.others")) { + sender.sendMessage(plugin.messages.noPermissionSetchatnamecolorOthers) + return true + } + + val targetPlayerName = args[1] + val targetPlayer = plugin.server.getPlayer(targetPlayerName) + if (targetPlayer == null) { + sender.sendMessage(plugin.messages.playerNotFound) + return true + } + + val targetMinecraftProfile = minecraftProfileService.getPreloadedMinecraftProfile(targetPlayer) + if (targetMinecraftProfile == null) { + sender.sendMessage(plugin.messages["no-minecraft-profile"]) + return true + } + + val targetMinecraftProfileId = targetMinecraftProfile.id + if (targetMinecraftProfileId == null) { + sender.sendMessage(plugin.messages["no-minecraft-profile"]) + return true + } + + setChatNameColorAsync(targetMinecraftProfileId, chatNameColor).thenRun { + sender.sendMessage(plugin.messages.setchatnamecolorChatNameColorHasBeenSetOthers) + }.exceptionally { exception -> + plugin.logger.severe("Failed to set chat name color for ${targetPlayer.name}") + plugin.logger.severe(exception.message) + sender.sendMessage("Failed to set chat name color.") + return@exceptionally null + } + return true + + } + + val minecraftProfileId = minecraftProfile.id + if (minecraftProfileId == null) { + sender.sendMessage(plugin.messages.noMinecraftProfile) + return true + } + + setChatNameColorAsync(minecraftProfileId, chatNameColor).thenRun { + sender.sendMessage(plugin.messages.setchatnamecolorChatNameColorHasBeenSet) + }.exceptionally { exception -> + plugin.logger.log(SEVERE, "Failed to set chat name color for ${minecraftProfile.name}", exception) + sender.sendMessage(plugin.messages.setchatnamecolorSomethingWentWrong) + return@exceptionally null + } + + sender.sendMessage(plugin.messages.setchatnamecolorChatNameColorHasBeenSet) + return true + } + + /** + * Checks if the input is a valid hex color code. A valid hex color code is a string that starts with a '#' followed by 6 characters that are either digits or letters from a to f. + * @param colorCode the color code to check + * @return true if the color code is valid, false otherwise + */ + private fun isHexColorCodeValid(colorCode: String): Boolean { + return colorCode.matches(Regex("#[0-9a-fA-F]{6}")) + } + + /** + * Sets the chat name color for a player asynchronously. + * @param minecraftProfileId the ID of the player's Minecraft profile + * @param chatNameColor the chat name color to set + * @return a string indicating the result of the operation + */ + private fun setChatNameColorAsync(minecraftProfileId: RPKMinecraftProfileId, chatNameColor: String) : CompletableFuture { + return runAsync { + plugin.database.getTable(RPKChatNameColorTable::class.java).upsert(minecraftProfileId, chatNameColor) + } + } +} \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/database/table/RPKChatNameColorTable.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/database/table/RPKChatNameColorTable.kt new file mode 100644 index 000000000..f88dd1653 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/database/table/RPKChatNameColorTable.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2021 Ren Binden + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rpkit.chat.bukkit.database.table + +import com.rpkit.chat.bukkit.RPKChatBukkit +import com.rpkit.chat.bukkit.database.create +import com.rpkit.chat.bukkit.database.jooq.Tables.RPKIT_CHAT_NAME_COLOR +import com.rpkit.core.database.Database +import com.rpkit.core.database.Table +import java.util.concurrent.CompletableFuture +import com.rpkit.chat.bukkit.database.jooq.tables.records.RpkitChatNameColorRecord +import com.rpkit.players.bukkit.profile.minecraft.RPKMinecraftProfileId +import java.util.concurrent.CompletableFuture.runAsync +import java.util.concurrent.CompletableFuture.supplyAsync +import java.util.logging.Level.SEVERE + +/** + * Represents the chat name color table. + */ +class RPKChatNameColorTable(private val database: Database, private val plugin: RPKChatBukkit) : Table { + + fun insert(id: RPKMinecraftProfileId?, chatNameColor: String): CompletableFuture { + // insert id -> chat name color record into database + return runAsync { + database.create + .insertInto( + RPKIT_CHAT_NAME_COLOR, + RPKIT_CHAT_NAME_COLOR.MINECRAFT_PROFILE_ID, + RPKIT_CHAT_NAME_COLOR.CHAT_NAME_COLOR + ) + .values( + id?.value, + chatNameColor + ) + .execute() + }.exceptionally { e -> + plugin.logger.log(SEVERE, "Failed to insert chat name color record for Minecraft profile ${id?.value} and chat name color $chatNameColor", e) + throw e + } + } + + fun update(minecraftProfileId: RPKMinecraftProfileId, chatNameColor: String): CompletableFuture { + // update chat name color record in database + return runAsync { + database.create + .update(RPKIT_CHAT_NAME_COLOR) + .set(RPKIT_CHAT_NAME_COLOR.CHAT_NAME_COLOR, chatNameColor) + .where(RPKIT_CHAT_NAME_COLOR.MINECRAFT_PROFILE_ID.eq(minecraftProfileId.value)) + .execute() + }.exceptionally { e -> + plugin.logger.log(SEVERE, "Failed to update chat name color record for Minecraft profile ${minecraftProfileId.value} and chat name color $chatNameColor", e) + throw e + } + } + + fun upsert(id: RPKMinecraftProfileId, chatNameColor: String): CompletableFuture { + return get(id).thenAcceptAsync { existingChatNameColor -> + if (existingChatNameColor == null) { + insert(id, chatNameColor) + } else { + update(id, chatNameColor) + } + } + } + + operator fun get(id: RPKMinecraftProfileId): CompletableFuture { + // get chat name color record from database by id + return supplyAsync { + database.create + .selectFrom(RPKIT_CHAT_NAME_COLOR) + .where(RPKIT_CHAT_NAME_COLOR.MINECRAFT_PROFILE_ID.eq(id.value)) + .fetchOne() + }.exceptionally { e -> + plugin.logger.log(SEVERE, "Failed to get chat name color record for Minecraft profile ${id.value}", e) + null + } + } + + fun delete(): CompletableFuture { + // delete chat name color record from database + return runAsync { + database.create + .deleteFrom(RPKIT_CHAT_NAME_COLOR) + .execute() + }.exceptionally { e -> + plugin.logger.log(SEVERE, "Failed to delete chat name color records", e) + throw e + } + } +} \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/messages/ChatMessages.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/messages/ChatMessages.kt index 6e38471f5..c8afc5434 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/messages/ChatMessages.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/messages/ChatMessages.kt @@ -278,4 +278,12 @@ class ChatMessages(plugin: RPKChatBukkit) : BukkitMessages(plugin) { val pluginAuthors = getParameterized("plugin-authors").let(::PluginAuthorsMessage) val pluginContributors = getParameterized("plugin-contributors").let(::PluginContributorsMessage) val serverVersion = getParameterized("server-version").let(::ServerVersionMessage) + val noPermissionSetchatnamecolor = get("no-permission-setchatnamecolor") + val setchatnamecolorUsage = get("setchatnamecolor-usage") + val setchatnamecolorInvalidColorCode = get("setchatnamecolor-invalid-color-code") + val setchatnamecolorChatNameColorHasBeenSet = get("setchatnamecolor-chat-name-color-has-been-set") + val setchatnamecolorSomethingWentWrong = get("setchatnamecolor-something-went-wrong") + val noPermissionSetchatnamecolorOthers = get("no-permission-setchatnamecolor-others") + val playerNotFound = get("player-not-found") + val setchatnamecolorChatNameColorHasBeenSetOthers = get("setchatnamecolor-chat-name-color-has-been-set-others") } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/mysql/V2__Chat_name_colors.sql b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/mysql/V2__Chat_name_colors.sql new file mode 100644 index 000000000..4ce2a4e59 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/mysql/V2__Chat_name_colors.sql @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Ren Binden + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- table to keep track of profile id -> chat name color +CREATE TABLE `rpkit_chat_name_color` +( + `minecraft_profile_id` int NOT NULL, + `chat_name_color` varchar(256) NOT NULL, + PRIMARY KEY (`minecraft_profile_id`) +); \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/sqlite/V2__Chat_name_colors.sql b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/sqlite/V2__Chat_name_colors.sql new file mode 100644 index 000000000..658d709a2 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/sqlite/V2__Chat_name_colors.sql @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Ren Binden + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- table to keep track of profile id -> chat name color +CREATE TABLE `rpkit_chat_name_color` +( + `minecraft_profile_id` int NOT NULL, + `chat_name_color` varchar(256) NOT NULL, + PRIMARY KEY (`minecraft_profile_id`) +); \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/resources/config.yml b/bukkit/rpk-chat-bukkit/src/main/resources/config.yml index bb1db814f..083b3f126 100644 --- a/bukkit/rpk-chat-bukkit/src/main/resources/config.yml +++ b/bukkit/rpk-chat-bukkit/src/main/resources/config.yml @@ -392,4 +392,6 @@ caching: minecraft_profile_id: enabled: true size: 20 - +misc: + characterChatNameColorOverrideEnabled: true + profileChatNameColorOverrideEnabled: true \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml b/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml index 02e9f864e..2687053eb 100644 --- a/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml +++ b/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml @@ -99,4 +99,13 @@ plugin-website: 'Website: ${website}' plugin-author: 'Author: ${author}' plugin-authors: 'Authors: ${authors}' plugin-contributors: 'Contributors: ${contributors}' -server-version: 'This server is running ${name} version ${version} (Implementing API version ${api_version})' \ No newline at end of file +server-version: 'This server is running ${name} version ${version} (Implementing API version ${api_version})' +no-permission-setchatnamecolor: '&cYou do not have permission to set the color of your name in chat.' +setchatnamecolor-usage: '&cUsage: /setchatnamecolor [color]' +setchatnamecolor-invalid-color-code: '&cThat color is not a valid color code. Expected something like #ffffff.' +setchatnamecolor-chat-name-color-has-been-set: '&aYour chat name color has been set.' +setchatnamecolor-something-went-wrong: '&cSomething went wrong while setting your chat name color.' +no-permission-setchatnamecolor-others: '&cYou do not have permission to set the color of other players\' names in chat.' +player-not-found: '&cNo player by that name was found.' +setchatnamecolor-chat-name-color-has-been-set: 'Your chat name color has been set.' +setchatnamecolor-chat-name-color-has-been-set-others: 'The chat name color of the specified player has been set.' \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/resources/plugin.yml b/bukkit/rpk-chat-bukkit/src/main/resources/plugin.yml index 5f9732344..21def9d96 100644 --- a/bukkit/rpk-chat-bukkit/src/main/resources/plugin.yml +++ b/bukkit/rpk-chat-bukkit/src/main/resources/plugin.yml @@ -54,6 +54,9 @@ commands: description: Reply to the last private message or chat group message sent to you aliases: [r] usage: / [message] + setchatnamecolor: + description: Sets the color of your name in chat + usage: / [color] permissions: rpkit.chat.command.listchatchannels: description: Allows listing chat channels @@ -100,3 +103,9 @@ permissions: rpkit.chat.command.reply: description: Allows replying to private messages default: true + rpkit.chat.command.setchatnamecolor: + description: Allows setting the color of your name in chat + default: true + rpkit.chat.command.setchatnamecolor.others: + description: Allows setting the color of other players' names in chat + default: op \ No newline at end of file diff --git a/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorService.kt b/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorService.kt new file mode 100644 index 000000000..79263f767 --- /dev/null +++ b/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatnamecolor/RPKChatNameColorService.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Ren Binden + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package com.rpkit.chat.bukkit.chatnamecolor + + import com.rpkit.core.service.Service + import com.rpkit.players.bukkit.profile.minecraft.RPKMinecraftProfile + + /** + * Provides operations related to overridden chat name colors + */ + interface RPKChatNameColorService : Service { + + /** + * Gets the chat name color for a player. + * If the player has no chat name color, null is returned. + * + * @param minecraftProfile The player to get the chat name color of + * @return The chat name color, or null if the player has no chat name color + */ + fun getChatNameColor(minecraftProfile: RPKMinecraftProfile?): String? + } + \ No newline at end of file diff --git a/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/snooper/RPKSnooperService.kt b/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/snooper/RPKSnooperService.kt index 1a5d73d5a..dc347b7d2 100644 --- a/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/snooper/RPKSnooperService.kt +++ b/bukkit/rpk-chat-lib-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/snooper/RPKSnooperService.kt @@ -27,12 +27,12 @@ interface RPKSnooperService : Service { /** * A list of all Minecraft profiles who are currently snooping. - * THis list is immutable, so Minecraft profiles should be added or removed with [addSnooper] and [removeSnooper] respectively. + * This list is immutable, so Minecraft profiles should be added or removed with [addSnooper] and [removeSnooper] respectively. */ val snoopers: CompletableFuture> /** - * Adds a snooper. This Minecraft profile is then abel to see messages they would not otherwise see. + * Adds a snooper. This Minecraft profile is then able to see messages they would not otherwise see. * * @param minecraftProfile The Minecraft profile to enable snooping for */