From f0a6916e26146595781dbc5100b7f26179597a9b Mon Sep 17 00:00:00 2001 From: dmccoystephenson Date: Sat, 6 Apr 2024 09:47:08 -0600 Subject: [PATCH 1/2] Added dev container --- .devcontainer/Dockerfile | 6 ++++++ .devcontainer/start_dev_container.bat | 4 ++++ .devcontainer/start_dev_container.sh | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/start_dev_container.bat create mode 100644 .devcontainer/start_dev_container.sh 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/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 From 72f08fcb4f56fa2f73900abee0e9d5155b7707aa Mon Sep 17 00:00:00 2001 From: dmccoystephenson Date: Sat, 6 Apr 2024 09:59:33 -0600 Subject: [PATCH 2/2] Added 'setchatnamecolor' command --- .../com/rpkit/chat/bukkit/RPKChatBukkit.kt | 3 + .../bukkit/chatchannel/RPKChatChannelImpl.kt | 26 ++++ .../format/part/GenericTextPart.kt | 2 +- .../SetChatNameColorCommand.kt | 126 ++++++++++++++++++ .../database/table/RPKChatNameColorTable.kt | 92 +++++++++++++ .../migrations/mysql/V2__Chat_name_colors.sql | 22 +++ .../sqlite/V2__Chat_name_colors.sql | 22 +++ .../src/main/resources/messages.yml | 8 +- .../src/main/resources/plugin.yml | 6 + 9 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/command/setchatnamecolor/SetChatNameColorCommand.kt create mode 100644 bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/database/table/RPKChatNameColorTable.kt create mode 100644 bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/mysql/V2__Chat_name_colors.sql create mode 100644 bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/sqlite/V2__Chat_name_colors.sql 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..bca831433 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.* @@ -182,6 +183,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 @@ -233,6 +235,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..15764136f 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,12 +21,14 @@ 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 import com.rpkit.chat.bukkit.context.DirectedPostFormatMessageContext import com.rpkit.chat.bukkit.context.DirectedPreFormatMessageContext import com.rpkit.chat.bukkit.context.UndirectedMessageContext +import com.rpkit.chat.bukkit.database.table.RPKChatNameColorTable import com.rpkit.chat.bukkit.event.chatchannel.RPKBukkitChatChannelMessageEvent import com.rpkit.chat.bukkit.mute.RPKChatChannelMuteService import com.rpkit.chat.bukkit.speaker.RPKChatChannelSpeakerService @@ -169,6 +171,9 @@ class RPKChatChannelImpl( format.flatMap { part -> part.toChatComponents(preFormatContext).join().toList() }.toTypedArray(), preFormatContext.isCancelled ) + if (senderMinecraftProfile != null) { + setSenderCharacterNamePartColor(senderMinecraftProfile) + }; directedPostFormatPipeline.forEach { component -> postFormatContext = component.process(postFormatContext).join() } @@ -190,4 +195,25 @@ class RPKChatChannelImpl( }) } + private fun setSenderCharacterNamePartColor(senderMinecraftProfile: RPKMinecraftProfile) { + val minecraftProfileId = senderMinecraftProfile.id ?: return + val recordExists = plugin.database.getTable(RPKChatNameColorTable::class.java)[minecraftProfileId].join() != null + if (recordExists) { + val senderCharacterNamePart = getSenderCharacterNamePart() ?: return + val chatNameColorRecord = plugin.database.getTable(RPKChatNameColorTable::class.java)[minecraftProfileId].join() + if (chatNameColorRecord != null) { + senderCharacterNamePart.color = chatNameColorRecord.chatNameColor + } + } + } + + private fun getSenderCharacterNamePart(): SenderCharacterNamePart? { + for (part in format) { + if (part is SenderCharacterNamePart) { + return part + } + } + return null + } + } \ No newline at end of file diff --git a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/GenericTextPart.kt b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/GenericTextPart.kt index 5d75c4aad..7f4bf80a1 100644 --- a/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/GenericTextPart.kt +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/chatchannel/format/part/GenericTextPart.kt @@ -30,7 +30,7 @@ import java.util.logging.Level abstract class GenericTextPart( private val plugin: RPKChatBukkit, val font: String? = null, - val color: String? = null, + var color: String? = null, val isBold: Boolean? = null, val isItalic: Boolean? = null, val isUnderlined: Boolean? = null, 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..6b0f0b613 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/command/setchatnamecolor/SetChatNameColorCommand.kt @@ -0,0 +1,126 @@ +/* + * 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 + +/** + * 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["no-permission-setchatnamecolor"]) + return true + } + if (sender !is Player) { + sender.sendMessage(plugin.messages["not-from-console"]) + return true + } + val minecraftProfileService = Services[RPKMinecraftProfileService::class.java] + if (minecraftProfileService == null) { + sender.sendMessage(plugin.messages["no-minecraft-profile-service"]) + return true + } + val minecraftProfile = minecraftProfileService.getPreloadedMinecraftProfile(sender) + if (minecraftProfile == null) { + sender.sendMessage(plugin.messages["no-minecraft-profile"]) + return true + } + + // if no arguments + if (args.isEmpty()) { + sender.sendMessage(plugin.messages["setchatnamecolor-usage"]) + return true + } + + // retrieve desired chat name color + val chatNameColor = args[0] + if (isInputTooLong(chatNameColor)) { + sender.sendMessage(plugin.messages["setchatnamecolor-too-long"]) + return true + } + + if (!isHexColorCodeValid(chatNameColor)) { + sender.sendMessage(plugin.messages["setchatnamecolor-invalid-color-code"]) + return true + } + + val minecraftProfileId = minecraftProfile.id + if (minecraftProfileId == null) { + sender.sendMessage(plugin.messages["no-minecraft-profile"]) + return true + } + + setChatNameColorAsync(minecraftProfileId, chatNameColor).thenRun { + sender.sendMessage(plugin.messages["setchatnamecolor-chat-name-color-has-been-set"]) + }.exceptionally { exception -> + plugin.logger.severe("Failed to set chat name color for ${minecraftProfile.name}") + plugin.logger.severe(exception.message) + sender.sendMessage(plugin.messages["setchatnamecolor-something-went-wrong"]) + return@exceptionally null + } + return true + } + + /** + * Checks if the input is too long. The maximum length for a chat name color is 16 characters. + * @param name the input to check + * @return true if the input is too long, false otherwise + */ + private fun isInputTooLong(name: String): Boolean { + return name.length > 16 + } + + /** + * 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 { + val recordExists = plugin.database.getTable(RPKChatNameColorTable::class.java)[minecraftProfileId].join() != null + if (recordExists) { + plugin.database.getTable(RPKChatNameColorTable::class.java).update(minecraftProfileId, chatNameColor) + } else { + plugin.database.getTable(RPKChatNameColorTable::class.java).insert(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..ad3b1dd14 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/kotlin/com/rpkit/chat/bukkit/database/table/RPKChatNameColorTable.kt @@ -0,0 +1,92 @@ +/* + * 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.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 + } + } + + operator fun get(id: RPKMinecraftProfileId): CompletableFuture { + // get chat name color record from database by id + return CompletableFuture.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/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..5d97870ee --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/mysql/V2__Chat_name_colors.sql @@ -0,0 +1,22 @@ +/* + * 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 +); \ 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..85bb37c85 --- /dev/null +++ b/bukkit/rpk-chat-bukkit/src/main/resources/com/rpkit/chat/migrations/sqlite/V2__Chat_name_colors.sql @@ -0,0 +1,22 @@ +/* + * 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 +); \ 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..07f8dbe0c 100644 --- a/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml +++ b/bukkit/rpk-chat-bukkit/src/main/resources/messages.yml @@ -99,4 +99,10 @@ 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-too-long: '&cThat color is too long. Expected something like #ffffff.' +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 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..736efc0e5 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,6 @@ 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