From ee66dddd99ad68005a1c876ca06d43588bb5d23b Mon Sep 17 00:00:00 2001 From: Caadiq Date: Mon, 29 Dec 2025 15:13:37 +0900 Subject: [PATCH] =?UTF-8?q?style:=20MessageUtils=20=EB=8F=84=EC=9E=85=20-?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=20=EC=B1=84=ED=8C=85=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MessageUtils 유틸리티 추가 (청록색 기본 + 흰색 강조) - 모든 명령어 및 GUI에 통일된 메시지 스타일 적용 - 수정 파일: SpawnCommand, NicknameCommand, CoordinateCommand, HeadCommand, PlayerCommand, ProtectFarmlandCommand, ChatCommand, TeleportGui, CoordinateGui, ChatEvents --- .../beemer/essentials/command/ChatCommand.kt | 29 +-- .../essentials/command/CoordinateCommand.kt | 76 ++---- .../beemer/essentials/command/HeadCommand.kt | 25 +- .../essentials/command/PlayerCommand.kt | 35 +-- .../command/ProtectFarmlandCommand.kt | 20 +- .../beemer/essentials/command/SpawnCommand.kt | 197 ++++++++------- .../com/beemer/essentials/event/ChatEvents.kt | 10 +- .../beemer/essentials/gui/CoordinateGui.kt | 12 +- .../com/beemer/essentials/gui/TeleportGui.kt | 238 +++++++++--------- .../essentials/nickname/NicknameCommand.kt | 50 +--- .../beemer/essentials/util/MessageUtils.kt | 118 +++++++++ 11 files changed, 395 insertions(+), 415 deletions(-) create mode 100644 Essentials/src/main/kotlin/com/beemer/essentials/util/MessageUtils.kt diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/ChatCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/ChatCommand.kt index cf7dcd9..5b2bb4a 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/ChatCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/ChatCommand.kt @@ -2,11 +2,10 @@ package com.beemer.essentials.command import com.beemer.essentials.config.ChatConfig import com.beemer.essentials.util.ChatUtils +import com.beemer.essentials.util.MessageUtils import com.mojang.brigadier.exceptions.CommandSyntaxException -import net.minecraft.ChatFormatting import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.Commands -import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerPlayer import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.RegisterCommandsEvent @@ -31,11 +30,7 @@ object ChatCommand { private fun hasPermissionOrSend(player: ServerPlayer?, requiredLevel: Int = 2): Boolean { if (player != null && !player.hasPermissions(requiredLevel)) { - player.sendSystemMessage( - Component.literal("해당 명령어를 실행할 권한이 없습니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "해당 명령어를 실행할 권한이 없습니다.") return false } return true @@ -51,14 +46,14 @@ object ChatCommand { ChatConfig.loadConfig() - val success = - Component.literal("채팅 형식을 새로고침했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - - if (player == null) - context.source.sendSuccess({ success }, false) - else player.sendSystemMessage(success) + if (player != null) { + MessageUtils.sendSuccess(player, "채팅 형식을 새로고침했습니다.") + } else { + context.source.sendSuccess( + { MessageUtils.success("채팅 형식을 새로고침했습니다.") }, + false + ) + } 1 } @@ -81,9 +76,7 @@ object ChatCommand { ChatUtils.clearChatForAll(allPlayers) server.playerList.broadcastSystemMessage( - Component.literal("\u00A0\u00A0채팅창을 비웠습니다.").withStyle { - it.withColor(ChatFormatting.AQUA) - }, + MessageUtils.info("채팅창을 비웠습니다."), false ) diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/CoordinateCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/CoordinateCommand.kt index c38bb6a..56a226a 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/CoordinateCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/CoordinateCommand.kt @@ -7,8 +7,8 @@ import com.beemer.essentials.data.Location import com.beemer.essentials.gui.Menu import com.beemer.essentials.gui.createPageContainer import com.beemer.essentials.util.DimensionUtils +import com.beemer.essentials.util.MessageUtils import com.mojang.brigadier.arguments.StringArgumentType -import net.minecraft.ChatFormatting import net.minecraft.commands.Commands import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerPlayer @@ -19,7 +19,7 @@ import net.neoforged.neoforge.event.RegisterCommandsEvent object CoordinateCommand { @SubscribeEvent fun onRegisterCommands(event: RegisterCommandsEvent) { - // /좌표 - GUI 열기 (페이지 0부터 시작) + // /좌표 - GUI 열기 event.dispatcher.register( Commands.literal("좌표").executes { context -> val player = context.source.playerOrException as ServerPlayer @@ -28,7 +28,7 @@ object CoordinateCommand { } ) - // /좌표이동 <장소> - 바로 이동 + // /좌표이동 <장소> event.dispatcher.register( Commands.literal("좌표이동") .then( @@ -70,16 +70,9 @@ object CoordinateCommand { ) if (CoordinateConfig.isExist(name)) { - player.sendSystemMessage( - Component.literal( - "$name 좌표가 이미 존재합니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .RED - ) - } + MessageUtils.sendError( + player, + "{$name} 좌표가 이미 존재합니다." ) return@executes 0 } @@ -116,16 +109,9 @@ object CoordinateCommand { ) CoordinateConfig.addCoordinate(coordinate) - player.sendSystemMessage( - Component.literal( - "$name 좌표를 추가했습니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .GOLD - ) - } + MessageUtils.sendSuccess( + player, + "{$name} 좌표를 추가했습니다." ) 1 } @@ -155,31 +141,17 @@ object CoordinateCommand { ) if (!CoordinateConfig.isExist(name)) { - player.sendSystemMessage( - Component.literal( - "$name 좌표가 존재하지 않습니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .RED - ) - } + MessageUtils.sendError( + player, + "{$name} 좌표가 존재하지 않습니다." ) return@executes 0 } CoordinateConfig.removeCoordinate(name) - player.sendSystemMessage( - Component.literal( - "$name 좌표를 제거했습니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .GOLD - ) - } + MessageUtils.sendSuccess( + player, + "{$name} 좌표를 제거했습니다." ) 1 } @@ -207,21 +179,13 @@ object CoordinateCommand { private fun teleportToCoordinate(player: ServerPlayer, name: String): Int { val coord = CoordinateConfig.getCoordinate(name) if (coord == null) { - player.sendSystemMessage( - Component.literal("$name 좌표가 존재하지 않습니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "{$name} 좌표가 존재하지 않습니다.") return 0 } val level = DimensionUtils.getLevelById(player.server, coord.dimension) if (level == null) { - player.sendSystemMessage( - Component.literal("존재하지 않는 차원입니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "존재하지 않는 차원입니다.") return 0 } @@ -244,11 +208,7 @@ object CoordinateCommand { ) player.teleportTo(level, coord.x, coord.y, coord.z, player.yRot, player.xRot) - player.sendSystemMessage( - Component.literal("$name (으)로 이동했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) + MessageUtils.sendSuccess(player, "{$name}(으)로 이동했습니다.") return 1 } } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/HeadCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/HeadCommand.kt index c5181fc..6e363db 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/HeadCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/HeadCommand.kt @@ -2,6 +2,7 @@ package com.beemer.essentials.command import com.beemer.essentials.config.PlayerConfig import com.beemer.essentials.nickname.NicknameDataStore +import com.beemer.essentials.util.MessageUtils import com.mojang.brigadier.arguments.StringArgumentType import java.util.Optional import java.util.UUID @@ -30,8 +31,6 @@ object HeadCommand { StringArgumentType.greedyString() ) .suggests { _, builder -> - // 서버에 접속한 적 있는 플레이어만 제안 - // 닉네임이 있으면 닉네임, 없으면 원래 이름 val suggestions = mutableListOf() PlayerConfig.getAllPlayers() @@ -93,11 +92,7 @@ object HeadCommand { // 3. 서버에 접속한 적 없는 플레이어면 거부 if (targetUuid == null || !PlayerConfig.isKnownPlayer(targetUuid)) { - player.sendSystemMessage( - Component.literal("해당 플레이어는 서버에 접속한 적이 없습니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "해당 플레이어는 서버에 접속한 적이 없습니다.") return 0 } @@ -130,21 +125,13 @@ object HeadCommand { val added = player.inventory.add(headItem) if (added) { - player.sendSystemMessage( - Component.literal(displayName) - .withStyle { it.withColor(ChatFormatting.GREEN) } - .append( - Component.literal("의 머리가 지급되었습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) + MessageUtils.sendSuccess(player, "{$displayName}의 머리가 지급되었습니다.") } else { // 인벤토리가 가득 찬 경우 바닥에 드랍 player.drop(headItem, false) - player.sendSystemMessage( - Component.literal("인벤토리가 가득 차서 ${displayName}의 머리를 바닥에 떨어뜨렸습니다.") - .withStyle { it.withColor(ChatFormatting.YELLOW) } + MessageUtils.sendWarning( + player, + "인벤토리가 가득 차서 {$displayName}의 머리를 바닥에 떨어뜨렸습니다." ) } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/PlayerCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/PlayerCommand.kt index 8219b48..8741cf9 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/PlayerCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/PlayerCommand.kt @@ -3,9 +3,8 @@ package com.beemer.essentials.command import com.beemer.essentials.config.PlayerConfig import com.beemer.essentials.util.CommandUtils import com.beemer.essentials.util.DimensionUtils -import net.minecraft.ChatFormatting +import com.beemer.essentials.util.MessageUtils import net.minecraft.commands.Commands -import net.minecraft.network.chat.Component import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.RegisterCommandsEvent @@ -23,16 +22,9 @@ object PlayerCommand { val lastLocation = info?.lastLocation ?: run { - player.sendSystemMessage( - Component.literal( - "이전 위치가 존재하지 않습니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .RED - ) - } + MessageUtils.sendError( + player, + "이전 위치가 존재하지 않습니다." ) return@executes 0 } @@ -43,16 +35,9 @@ object PlayerCommand { lastLocation.dimension ) ?: run { - player.sendSystemMessage( - Component.literal( - "존재하지 않는 차원입니다." - ) - .withStyle { - it.withColor( - ChatFormatting - .RED - ) - } + MessageUtils.sendError( + player, + "존재하지 않는 차원입니다." ) return@executes 0 } @@ -65,11 +50,7 @@ object PlayerCommand { player.yRot, player.xRot ) - player.sendSystemMessage( - Component.literal("이전 위치로 이동했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) + MessageUtils.sendSuccess(player, "이전 위치로 이동했습니다.") 1 } ) diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/ProtectFarmlandCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/ProtectFarmlandCommand.kt index 7453a51..4124427 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/ProtectFarmlandCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/ProtectFarmlandCommand.kt @@ -2,9 +2,8 @@ package com.beemer.essentials.command import com.beemer.essentials.config.ProtectFarmlandConfig import com.beemer.essentials.util.CommandUtils -import net.minecraft.ChatFormatting +import com.beemer.essentials.util.MessageUtils import net.minecraft.commands.Commands -import net.minecraft.network.chat.Component import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.RegisterCommandsEvent @@ -19,22 +18,9 @@ object ProtectFarmlandCommand { ?: return@executes 0 val enabled = ProtectFarmlandConfig.toggle() + val status = if (enabled) "활성화" else "비활성화" - player.sendSystemMessage( - Component.literal("밭 보호를 ") - .withStyle { it.withColor(ChatFormatting.GOLD) } - .append( - Component.literal(if (enabled) "활성화" else "비활성화") - .withStyle { - it.withColor(ChatFormatting.DARK_GREEN) - } - ) - .append( - Component.literal("했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) + MessageUtils.sendSuccess(player, "밭 보호를 {$status}했습니다.") 1 } ) diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/command/SpawnCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/command/SpawnCommand.kt index 5cf5517..54cce97 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/command/SpawnCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/command/SpawnCommand.kt @@ -5,116 +5,129 @@ import com.beemer.essentials.config.SpawnConfig import com.beemer.essentials.data.Location import com.beemer.essentials.util.CommandUtils import com.beemer.essentials.util.DimensionUtils -import net.minecraft.ChatFormatting +import com.beemer.essentials.util.MessageUtils import net.minecraft.commands.Commands -import net.minecraft.network.chat.Component import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.RegisterCommandsEvent object SpawnCommand { - @SubscribeEvent - fun onRegisterCommands(event: RegisterCommandsEvent) { - listOf("setspawn", "스폰설정").forEach { command -> - event.dispatcher.register( - Commands.literal(command).executes { context -> - val player = - CommandUtils.getPlayerOrSendFailure(context.source) - ?: return@executes 0 + @SubscribeEvent + fun onRegisterCommands(event: RegisterCommandsEvent) { + listOf("setspawn", "스폰설정").forEach { command -> + event.dispatcher.register( + Commands.literal(command).executes { context -> + val player = + CommandUtils.getPlayerOrSendFailure(context.source) + ?: return@executes 0 - val exactPos = player.position() - val blockPos = player.blockPosition() - val dimensionId = player.level().dimension().location().toString() - val biomeId = - player.level() - .getBiome(blockPos) - .unwrapKey() - .map { it.location().toString() } - .orElse("minecraft:plains") + val exactPos = player.position() + val blockPos = player.blockPosition() + val dimension = + player.level().dimension().location().toString() + val biome = + player.level() + .getBiome(blockPos) + .unwrapKey() + .map { it.location().toString() } + .orElse("minecraft:plains") - val location = - Location( - dimension = dimensionId, - biome = biomeId, - x = exactPos.x, - y = exactPos.y, - z = exactPos.z - ) + val location = + Location( + dimension = dimension, + biome = biome, + x = exactPos.x, + y = exactPos.y, + z = exactPos.z + ) - SpawnConfig.setCustomSpawn(location) - - player.sendSystemMessage( - Component.literal("스폰 지점이 현재 위치로 설정되었습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) + SpawnConfig.setCustomSpawn(location) + MessageUtils.sendSuccess(player, "스폰 지점이 현재 위치로 설정되었습니다.") + 1 } ) - 1 - } - ) - } + } - listOf("spawn", "스폰", "넴주").forEach { command -> - event.dispatcher.register( - Commands.literal(command).executes { context -> - val player = - CommandUtils.getPlayerOrSendFailure(context.source) - ?: return@executes 0 + listOf("spawn", "스폰", "넴주").forEach { command -> + event.dispatcher.register( + Commands.literal(command).executes { context -> + val player = + CommandUtils.getPlayerOrSendFailure(context.source) + ?: return@executes 0 - val target = SpawnConfig.getCustomSpawn() ?: SpawnConfig.getDefaultSpawn() + val target = + SpawnConfig.getCustomSpawn() + ?: SpawnConfig.getDefaultSpawn() - target?.let { t -> - val level = DimensionUtils.getLevelById(player.server, t.dimension) + target?.let { t -> + val level = + DimensionUtils.getLevelById( + player.server, + t.dimension + ) - level?.let { l -> - val exactPos = player.position() - val blockPos = player.blockPosition() - val currentDimension = - player.level().dimension().location().toString() - val currentBiome = - player.level() - .getBiome(blockPos) - .unwrapKey() - .map { it.location().toString() } - .orElse("minecraft:plains") + level?.let { l -> + val exactPos = player.position() + val blockPos = player.blockPosition() + val currentDimension = + player.level() + .dimension() + .location() + .toString() + val currentBiome = + player.level() + .getBiome(blockPos) + .unwrapKey() + .map { + it.location() + .toString() + } + .orElse("minecraft:plains") - val currentLocation = - Location( - dimension = currentDimension, - biome = currentBiome, - x = exactPos.x, - y = exactPos.y, - z = exactPos.z - ) + val currentLocation = + Location( + dimension = + currentDimension, + biome = currentBiome, + x = exactPos.x, + y = exactPos.y, + z = exactPos.z + ) - PlayerConfig.recordLastLocation(player, currentLocation) - player.teleportTo(l, t.x, t.y, t.z, player.yRot, player.xRot) - player.sendSystemMessage( - Component.literal("스폰으로 이동했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) + PlayerConfig.recordLastLocation( + player, + currentLocation + ) + player.teleportTo( + l, + t.x, + t.y, + t.z, + player.yRot, + player.xRot + ) + MessageUtils.sendSuccess( + player, + "스폰으로 이동했습니다." + ) + } } - ) - } - } - 1 - } - ) - } - - listOf("delspawn", "스폰삭제").forEach { command -> - event.dispatcher.register( - Commands.literal(command).executes { context -> - val player = - CommandUtils.getPlayerOrSendFailure(context.source) - ?: return@executes 0 - - SpawnConfig.removeCustomSpawn() - player.sendSystemMessage( - Component.literal("스폰 지점이 기본 스폰 지점으로 변경되었습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) + 1 } ) - 1 - } - ) + } + + listOf("delspawn", "스폰삭제").forEach { command -> + event.dispatcher.register( + Commands.literal(command).executes { context -> + val player = + CommandUtils.getPlayerOrSendFailure(context.source) + ?: return@executes 0 + + SpawnConfig.removeCustomSpawn() + MessageUtils.sendInfo(player, "스폰 지점이 기본 스폰 지점으로 변경되었습니다.") + 1 + } + ) + } } - } } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/event/ChatEvents.kt b/Essentials/src/main/kotlin/com/beemer/essentials/event/ChatEvents.kt index 4522552..5cc47ff 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/event/ChatEvents.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/event/ChatEvents.kt @@ -1,7 +1,7 @@ package com.beemer.essentials.event import com.beemer.essentials.util.ChatUtils -import net.minecraft.network.chat.Component +import com.beemer.essentials.util.MessageUtils import net.minecraft.server.level.ServerPlayer import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.ServerChatEvent @@ -12,13 +12,11 @@ object ChatEvents { val player = event.player as ServerPlayer val raw = event.rawText - if (raw.startsWith("/")) - return + if (raw.startsWith("/")) return if (ChatUtils.hasIllegalCharacter(raw)) { event.isCanceled = true - player.sendSystemMessage(Component.literal("채팅에 허용되지 않는 문자가 포함되어 있습니다.") - .withStyle { it.withColor(net.minecraft.ChatFormatting.RED) }) + MessageUtils.sendError(player, "채팅에 허용되지 않는 문자가 포함되어 있습니다.") return } @@ -26,4 +24,4 @@ object ChatEvents { event.isCanceled = true } -} \ No newline at end of file +} diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/gui/CoordinateGui.kt b/Essentials/src/main/kotlin/com/beemer/essentials/gui/CoordinateGui.kt index b782b79..32389b1 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/gui/CoordinateGui.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/gui/CoordinateGui.kt @@ -4,6 +4,7 @@ import com.beemer.essentials.config.CoordinateConfig import com.beemer.essentials.config.PlayerConfig import com.beemer.essentials.data.Location import com.beemer.essentials.nickname.NicknameDataStore +import com.beemer.essentials.util.MessageUtils import com.beemer.essentials.util.TranslationUtils.translateBiome import com.beemer.essentials.util.TranslationUtils.translateDimension import java.util.UUID @@ -109,6 +110,7 @@ class Menu( val x = tag.getDouble("x") val y = tag.getDouble("y") val z = tag.getDouble("z") + val coordName = tag.getString("name") val dimension = ResourceLocation.tryParse(tag.getString("dimension"))?.let { ResourceKey.create(Registries.DIMENSION, it) @@ -135,15 +137,7 @@ class Menu( ) player.teleportTo(level, x, y, z, player.yRot, player.xRot) - player.sendSystemMessage( - Component.literal(tag.getString("name")) - .withStyle { it.withColor(ChatFormatting.DARK_GREEN) } - .append( - Component.literal("(으)로 이동했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) + MessageUtils.sendSuccess(player, "{$coordName}(으)로 이동했습니다.") } player.closeContainer() } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/gui/TeleportGui.kt b/Essentials/src/main/kotlin/com/beemer/essentials/gui/TeleportGui.kt index 2145e1e..f1feea1 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/gui/TeleportGui.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/gui/TeleportGui.kt @@ -4,8 +4,7 @@ import com.beemer.essentials.config.PlayerConfig import com.beemer.essentials.data.Location import com.beemer.essentials.data.Player import com.beemer.essentials.nickname.NicknameDataStore -import net.minecraft.ChatFormatting -import net.minecraft.network.chat.Component +import com.beemer.essentials.util.MessageUtils import net.minecraft.server.level.ServerPlayer import net.minecraft.world.Container import net.minecraft.world.entity.player.Inventory @@ -21,140 +20,127 @@ class TeleportGui( val container: Container, val viewer: ServerPlayer ) : AbstractContainerMenu(MenuType.GENERIC_9x3, syncId) { - companion object { - const val CONTAINER_COLUMNS = 9 - const val CONTAINER_ROWS = 3 - const val CONTAINER_SIZE = CONTAINER_COLUMNS * CONTAINER_ROWS + companion object { + const val CONTAINER_COLUMNS = 9 + const val CONTAINER_ROWS = 3 + const val CONTAINER_SIZE = CONTAINER_COLUMNS * CONTAINER_ROWS - const val SLOT_SIZE = 18 - const val LEFT_PADDING = 8 - const val TOP_PADDING = 18 + const val SLOT_SIZE = 18 + const val LEFT_PADDING = 8 + const val TOP_PADDING = 18 - const val PLAYER_INV_TOP = 86 - const val HOTBAR_Y = 144 - } - - private val targetPlayers = - viewer.server - .playerList - .players - .filterIsInstance() - .filter { it.uuid != viewer.uuid } - .map { it.uuid } - - init { - for (i in 0 until CONTAINER_SIZE) { - val x = LEFT_PADDING + (i % CONTAINER_COLUMNS) * SLOT_SIZE - val y = TOP_PADDING + (i / CONTAINER_COLUMNS) * SLOT_SIZE - addSlot( - object : Slot(container, i, x, y) { - override fun mayPickup( - player: net.minecraft.world.entity.player.Player - ): Boolean = false - override fun mayPlace(stack: ItemStack): Boolean = false - } - ) + const val PLAYER_INV_TOP = 86 + const val HOTBAR_Y = 144 } - for (row in 0 until 3) { - for (col in 0 until 9) { - val index = col + row * 9 + 9 - val x = LEFT_PADDING + col * SLOT_SIZE - val y = PLAYER_INV_TOP + row * SLOT_SIZE - addSlot(Slot(playerInv, index, x, y)) - } - } + private val targetPlayers = + viewer.server + .playerList + .players + .filterIsInstance() + .filter { it.uuid != viewer.uuid } + .map { it.uuid } - for (col in 0 until 9) { - val x = LEFT_PADDING + col * SLOT_SIZE - val y = HOTBAR_Y - addSlot(Slot(playerInv, col, x, y)) - } - } - - override fun clicked( - slotId: Int, - button: Int, - clickType: ClickType, - player: net.minecraft.world.entity.player.Player - ) { - if (slotId in 0 until CONTAINER_SIZE && player is ServerPlayer) { - val currentTargetPlayer = - targetPlayers.getOrNull(slotId)?.let { player.server.playerList.getPlayer(it) } - - if (currentTargetPlayer != null && player.uuid != currentTargetPlayer.uuid) { - val prevPos = player.blockPosition() - val prevDimension = player.level().dimension().location().toString() - val prevBiome = - player.level() - .getBiome(prevPos) - .unwrapKey() - .map { it.location().toString() } - .orElse("minecraft:plains") - val previousLocation = - Location( - dimension = prevDimension, - biome = prevBiome, - x = prevPos.x.toDouble(), - y = prevPos.y.toDouble(), - z = prevPos.z.toDouble() + init { + for (i in 0 until CONTAINER_SIZE) { + val x = LEFT_PADDING + (i % CONTAINER_COLUMNS) * SLOT_SIZE + val y = TOP_PADDING + (i / CONTAINER_COLUMNS) * SLOT_SIZE + addSlot( + object : Slot(container, i, x, y) { + override fun mayPickup( + player: net.minecraft.world.entity.player.Player + ): Boolean = false + override fun mayPlace(stack: ItemStack): Boolean = false + } ) - PlayerConfig.recordLastLocation(player, previousLocation) + } - val targetLevel = currentTargetPlayer.serverLevel() - - player.teleportTo( - targetLevel, - currentTargetPlayer.x, - currentTargetPlayer.y, - currentTargetPlayer.z, - currentTargetPlayer.yRot, - currentTargetPlayer.xRot - ) - - // 닉네임이 있으면 닉네임 사용 - val targetName = - NicknameDataStore.getNickname(currentTargetPlayer.uuid) - ?: currentTargetPlayer.gameProfile.name - val playerName = - NicknameDataStore.getNickname(player.uuid) ?: player.gameProfile.name - - player.sendSystemMessage( - Component.literal(targetName) - .withStyle { it.withColor(ChatFormatting.AQUA) } - .append( - Component.literal("님에게 텔레포트 했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) - currentTargetPlayer.sendSystemMessage( - Component.literal(playerName) - .withStyle { it.withColor(ChatFormatting.AQUA) } - .append( - Component.literal("님이 당신에게 텔레포트 했습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) - } else { - player.sendSystemMessage( - Component.literal("해당 플레이어는 현재 오프라인입니다.").withStyle { - it.withColor(ChatFormatting.RED) + for (row in 0 until 3) { + for (col in 0 until 9) { + val index = col + row * 9 + 9 + val x = LEFT_PADDING + col * SLOT_SIZE + val y = PLAYER_INV_TOP + row * SLOT_SIZE + addSlot(Slot(playerInv, index, x, y)) } - ) - } + } - player.closeContainer() - return + for (col in 0 until 9) { + val x = LEFT_PADDING + col * SLOT_SIZE + val y = HOTBAR_Y + addSlot(Slot(playerInv, col, x, y)) + } } - super.clicked(slotId, button, clickType, player) - } - override fun stillValid(player: net.minecraft.world.entity.player.Player): Boolean = true + override fun clicked( + slotId: Int, + button: Int, + clickType: ClickType, + player: net.minecraft.world.entity.player.Player + ) { + if (slotId in 0 until CONTAINER_SIZE && player is ServerPlayer) { + val currentTargetPlayer = + targetPlayers.getOrNull(slotId)?.let { + player.server.playerList.getPlayer(it) + } - override fun quickMoveStack( - player: net.minecraft.world.entity.player.Player, - index: Int - ): ItemStack = ItemStack.EMPTY + if (currentTargetPlayer != null && player.uuid != currentTargetPlayer.uuid + ) { + val prevPos = player.blockPosition() + val prevDimension = player.level().dimension().location().toString() + val prevBiome = + player.level() + .getBiome(prevPos) + .unwrapKey() + .map { it.location().toString() } + .orElse("minecraft:plains") + val previousLocation = + Location( + dimension = prevDimension, + biome = prevBiome, + x = prevPos.x.toDouble(), + y = prevPos.y.toDouble(), + z = prevPos.z.toDouble() + ) + PlayerConfig.recordLastLocation(player, previousLocation) + + val targetLevel = currentTargetPlayer.serverLevel() + + player.teleportTo( + targetLevel, + currentTargetPlayer.x, + currentTargetPlayer.y, + currentTargetPlayer.z, + currentTargetPlayer.yRot, + currentTargetPlayer.xRot + ) + + // 닉네임이 있으면 닉네임 사용 + val targetName = + NicknameDataStore.getNickname(currentTargetPlayer.uuid) + ?: currentTargetPlayer.gameProfile.name + val playerName = + NicknameDataStore.getNickname(player.uuid) + ?: player.gameProfile.name + + MessageUtils.sendSuccess(player, "{$targetName}님에게 텔레포트 했습니다.") + MessageUtils.sendInfo( + currentTargetPlayer, + "{$playerName}님이 당신에게 텔레포트 했습니다." + ) + } else { + MessageUtils.sendError(player, "해당 플레이어는 현재 오프라인입니다.") + } + + player.closeContainer() + return + } + super.clicked(slotId, button, clickType, player) + } + + override fun stillValid(player: net.minecraft.world.entity.player.Player): Boolean = true + + override fun quickMoveStack( + player: net.minecraft.world.entity.player.Player, + index: Int + ): ItemStack = ItemStack.EMPTY } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/nickname/NicknameCommand.kt b/Essentials/src/main/kotlin/com/beemer/essentials/nickname/NicknameCommand.kt index 7966a2a..5186af2 100644 --- a/Essentials/src/main/kotlin/com/beemer/essentials/nickname/NicknameCommand.kt +++ b/Essentials/src/main/kotlin/com/beemer/essentials/nickname/NicknameCommand.kt @@ -1,9 +1,8 @@ package com.beemer.essentials.nickname +import com.beemer.essentials.util.MessageUtils import com.mojang.brigadier.arguments.StringArgumentType -import net.minecraft.ChatFormatting import net.minecraft.commands.Commands -import net.minecraft.network.chat.Component import net.minecraft.server.level.ServerPlayer import net.neoforged.bus.api.SubscribeEvent import net.neoforged.neoforge.event.RegisterCommandsEvent @@ -29,7 +28,6 @@ object NicknameCommand { .entity as? ServerPlayer ?: return@executes 0 - val nickname = StringArgumentType .getString( @@ -46,7 +44,6 @@ object NicknameCommand { val player = context.source.entity as? ServerPlayer ?: return@executes 0 - executeReset(player) } ) @@ -69,7 +66,6 @@ object NicknameCommand { .entity as? ServerPlayer ?: return@executes 0 - val nickname = StringArgumentType .getString( @@ -86,7 +82,6 @@ object NicknameCommand { val player = context.source.entity as? ServerPlayer ?: return@executes 0 - executeReset(player) } ) @@ -96,65 +91,34 @@ object NicknameCommand { private fun executeSet(player: ServerPlayer, nickname: String): Int { // 유효성 검사: 길이 if (nickname.length < 2 || nickname.length > 16) { - player.sendSystemMessage( - Component.literal("닉네임은 2~16자 사이여야 합니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "닉네임은 2~16자 사이여야 합니다.") return 0 } // 유효성 검사: 중복 if (NicknameDataStore.isNicknameTaken(nickname, player.uuid)) { - player.sendSystemMessage( - Component.literal("이미 사용 중인 닉네임입니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "이미 사용 중인 닉네임입니다.") return 0 } - // 닉네임 저장 및 적용 (gameProfile.name = 실제 마인크래프트 이름) + // 닉네임 저장 및 적용 NicknameDataStore.setNickname(player.uuid, player.gameProfile.name, nickname) NicknameManager.applyNickname(player, nickname) - player.sendSystemMessage( - Component.literal("닉네임이 ") - .withStyle { it.withColor(ChatFormatting.GOLD) } - .append( - Component.literal(nickname).withStyle { - it.withColor(ChatFormatting.AQUA) - } - ) - .append( - Component.literal("(으)로 변경되었습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - ) - + MessageUtils.sendSuccess(player, "닉네임이 {$nickname}(으)로 변경되었습니다.") return 1 } private fun executeReset(player: ServerPlayer): Int { if (!NicknameDataStore.hasNickname(player.uuid)) { - player.sendSystemMessage( - Component.literal("설정된 닉네임이 없습니다.").withStyle { - it.withColor(ChatFormatting.RED) - } - ) + MessageUtils.sendError(player, "설정된 닉네임이 없습니다.") return 0 } NicknameDataStore.removeNickname(player.uuid) NicknameManager.removeNickname(player) - player.sendSystemMessage( - Component.literal("닉네임이 초기화되었습니다.").withStyle { - it.withColor(ChatFormatting.GOLD) - } - ) - + MessageUtils.sendSuccess(player, "닉네임이 초기화되었습니다.") return 1 } } diff --git a/Essentials/src/main/kotlin/com/beemer/essentials/util/MessageUtils.kt b/Essentials/src/main/kotlin/com/beemer/essentials/util/MessageUtils.kt new file mode 100644 index 0000000..0b06692 --- /dev/null +++ b/Essentials/src/main/kotlin/com/beemer/essentials/util/MessageUtils.kt @@ -0,0 +1,118 @@ +package com.beemer.essentials.util + +import net.minecraft.network.chat.Component +import net.minecraft.network.chat.MutableComponent +import net.minecraft.network.chat.Style +import net.minecraft.network.chat.TextColor +import net.minecraft.server.level.ServerPlayer + +/** 메시지 스타일 유틸리티 기본 텍스트: 밝은 청록색 (#A8D8D8) 강조 텍스트: 흰색 (#FFFFFF) */ +object MessageUtils { + + // 기본 색상 (더 밝게) + private const val BASE_COLOR = 0xA8D8D8 // 밝은 청록색 (기본 텍스트) + private const val ACCENT_COLOR = 0xFFFFFF // 흰색 (강조) + private const val ERROR_COLOR = 0xFF8888 // 밝은 빨간색 (오류) + private const val WARNING_COLOR = 0xFFE066 // 밝은 노란색 (경고) + + /** + * 기본 스타일 메시지 생성 + * @param text 텍스트 (강조 부분은 {중괄호}로 감싸기) 예: "스폰으로 이동했습니다." 또는 "{비머}님에게 텔레포트 요청을 보냈습니다." + */ + fun styled(text: String): MutableComponent { + return parseStyledText(text, BASE_COLOR, ACCENT_COLOR) + } + + /** 성공 메시지 (아이콘 없이 기본 스타일) */ + fun success(text: String): MutableComponent { + return styled(text) + } + + /** 정보 메시지 (아이콘 없이 기본 스타일) */ + fun info(text: String): MutableComponent { + return styled(text) + } + + /** 오류 메시지 (빨간색 스타일) */ + fun error(text: String): MutableComponent { + return parseStyledText(text, ERROR_COLOR, ACCENT_COLOR) + } + + /** 경고 메시지 (노란색 스타일) */ + fun warning(text: String): MutableComponent { + return parseStyledText(text, WARNING_COLOR, ACCENT_COLOR) + } + + // === 플레이어에게 직접 전송 === + + fun sendSuccess(player: ServerPlayer, text: String) { + player.sendSystemMessage(success(text)) + } + + fun sendInfo(player: ServerPlayer, text: String) { + player.sendSystemMessage(info(text)) + } + + fun sendError(player: ServerPlayer, text: String) { + player.sendSystemMessage(error(text)) + } + + fun sendWarning(player: ServerPlayer, text: String) { + player.sendSystemMessage(warning(text)) + } + + /** 스타일 텍스트 파싱 {중괄호} 안의 텍스트는 강조 색상으로 표시 */ + private fun parseStyledText(text: String, baseColor: Int, accentColor: Int): MutableComponent { + val result: MutableComponent = Component.empty() + var i = 0 + val sb = StringBuilder() + + while (i < text.length) { + when { + text[i] == '{' -> { + // 이전 텍스트 추가 (기본 색상) + if (sb.isNotEmpty()) { + result.append( + Component.literal(sb.toString()) + .withStyle( + Style.EMPTY.withColor(TextColor.fromRgb(baseColor)) + ) + ) + sb.clear() + } + // 강조 텍스트 찾기 + val endIdx = text.indexOf('}', i) + if (endIdx != -1) { + val accentText = text.substring(i + 1, endIdx) + result.append( + Component.literal(accentText) + .withStyle( + Style.EMPTY.withColor( + TextColor.fromRgb(accentColor) + ) + ) + ) + i = endIdx + 1 + } else { + sb.append(text[i]) + i++ + } + } + else -> { + sb.append(text[i]) + i++ + } + } + } + + // 남은 텍스트 추가 + if (sb.isNotEmpty()) { + result.append( + Component.literal(sb.toString()) + .withStyle(Style.EMPTY.withColor(TextColor.fromRgb(baseColor))) + ) + } + + return result + } +}