From 74cc047dd77c80a31bf677d02256575b138992ce Mon Sep 17 00:00:00 2001 From: Atrius Date: Sun, 17 May 2026 15:01:16 -0400 Subject: [PATCH 1/3] fix: compose existing requirement with permission check in BaseCommand Brigadier's ArgumentBuilder.requires() overwrites the existing predicate instead of chaining them. This caused subcommand requirements (e.g., 'sender is Player') to be lost when BaseCommand applied the permission check, allowing console to reach executes blocks that expected a Player. Fix by capturing the existing requirement before applying the permission check and composing both predicates with &&. --- src/main/kotlin/xyz/atrius/waystones/command/BaseCommand.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/xyz/atrius/waystones/command/BaseCommand.kt b/src/main/kotlin/xyz/atrius/waystones/command/BaseCommand.kt index 863ff419..8c1433da 100644 --- a/src/main/kotlin/xyz/atrius/waystones/command/BaseCommand.kt +++ b/src/main/kotlin/xyz/atrius/waystones/command/BaseCommand.kt @@ -24,7 +24,8 @@ abstract class BaseCommand( .build(literal(command.name)) command.basePermission?.let { permission -> - subCommand.requires { it.sender.hasPermission(permission) } + val existing = subCommand.requirement + subCommand.requires { (existing == null || existing.test(it)) && it.sender.hasPermission(permission) } } base.then(subCommand) From a74fb0f2d2a3c2e24a4ba295829cc12497945816 Mon Sep 17 00:00:00 2001 From: Atrius Date: Sun, 17 May 2026 15:27:36 -0400 Subject: [PATCH 2/3] chore: add localized sender type name utility Add senderTypeName() extension function to CommandUtils that maps CommandSender types to localized, human-readable names. Supports ConsoleCommandSender, BlockCommandSender, RemoteConsoleCommandSender, Player, and unknown sender types. Add corresponding locale keys for English and Chinese translations. --- .../atrius/waystones/utility/CommandUtils.kt | 19 +++++++++++++++++++ src/main/resources/locale-en.yml | 7 ++++++- src/main/resources/locale-zh.yml | 5 +++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/xyz/atrius/waystones/utility/CommandUtils.kt b/src/main/kotlin/xyz/atrius/waystones/utility/CommandUtils.kt index 9c34a1b4..a8aaaa5c 100644 --- a/src/main/kotlin/xyz/atrius/waystones/utility/CommandUtils.kt +++ b/src/main/kotlin/xyz/atrius/waystones/utility/CommandUtils.kt @@ -2,6 +2,12 @@ package xyz.atrius.waystones.utility import com.mojang.brigadier.context.CommandContext import io.papermc.paper.command.brigadier.CommandSourceStack +import org.bukkit.command.BlockCommandSender +import org.bukkit.command.CommandSender +import org.bukkit.command.ConsoleCommandSender +import org.bukkit.command.RemoteConsoleCommandSender +import org.bukkit.entity.Player +import xyz.atrius.waystones.manager.LocalizationManager inline fun CommandContext.getArgument(name: String): T = getArgument(name, T::class.java) @@ -28,3 +34,16 @@ fun CommandContext.getArguments( } } } + +fun CommandSender.senderTypeName(localization: LocalizationManager): String { + val player = this as? Player + val message = when (this) { + is RemoteConsoleCommandSender -> localization["sender-type-rcon"] + is ConsoleCommandSender -> localization["sender-type-console"] + is BlockCommandSender -> localization["sender-type-block"] + is Player -> localization["sender-type-player"] + else -> localization["sender-type-unknown"] + } + + return message.format(player) +} diff --git a/src/main/resources/locale-en.yml b/src/main/resources/locale-en.yml index 334cb728..5838a8dd 100644 --- a/src/main/resources/locale-en.yml +++ b/src/main/resources/locale-en.yml @@ -108,4 +108,9 @@ migrate-waystone-migrations-failed: "&c{0} waystone {0,choice,1#migration|1 Date: Sun, 17 May 2026 15:28:15 -0400 Subject: [PATCH 3/3] fix: replace unsafe Player casts with safe casts and error messages Extract executeWithPlayer() helper in KeyCommand to eliminate duplicated player-check-and-error logic. Replace 'as Player' hard casts with safe 'as? Player' casts that return a localized error message when the sender is not a player. Also fix unsafe 'holder as Player' cast in Item.kt addItemNaturally() with a safe cast that returns early if the holder is not a Player. --- .../waystones/command/waystones/KeyCommand.kt | 23 +++++++++++++------ .../xyz/atrius/waystones/utility/Item.kt | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/xyz/atrius/waystones/command/waystones/KeyCommand.kt b/src/main/kotlin/xyz/atrius/waystones/command/waystones/KeyCommand.kt index 65ccba84..bdcc5e10 100644 --- a/src/main/kotlin/xyz/atrius/waystones/command/waystones/KeyCommand.kt +++ b/src/main/kotlin/xyz/atrius/waystones/command/waystones/KeyCommand.kt @@ -3,6 +3,7 @@ package xyz.atrius.waystones.command.waystones import com.mojang.brigadier.Command import com.mojang.brigadier.arguments.IntegerArgumentType import com.mojang.brigadier.builder.ArgumentBuilder +import com.mojang.brigadier.context.CommandContext import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.Commands.argument import io.papermc.paper.command.brigadier.argument.ArgumentTypes @@ -14,6 +15,7 @@ import xyz.atrius.waystones.manager.LocalizationManager import xyz.atrius.waystones.provider.DefaultKeyProvider import xyz.atrius.waystones.utility.getArgument import xyz.atrius.waystones.utility.message +import xyz.atrius.waystones.utility.senderTypeName @Single class KeyCommand( @@ -28,17 +30,12 @@ class KeyCommand( override fun build(base: ArgumentBuilder): ArgumentBuilder { val base = base .requires { it.sender is Player } - .executes { - val sender = it.source.sender as Player - command(sender, 1, sender) - } + .executes { executeWithPlayer(it, 1) } val count = argument("count", IntegerArgumentType.integer()) .requires { it.sender is Player } .executes { - val sender = it.source.sender as Player val amount = it.getArgument("count") - - command(sender, amount, sender) + executeWithPlayer(it, amount) } val target = argument("target", ArgumentTypes.player()) .requires { it.sender.hasPermission("waystones.getkey.all") } @@ -56,6 +53,18 @@ class KeyCommand( return base.then(count) } + private fun executeWithPlayer(context: CommandContext, amount: Int): Int { + val sender = context.source.sender + val player = sender as? Player + + if (player == null) { + sender.message(localization["command-bad-sender", sender.senderTypeName(localization)]) + return Command.SINGLE_SUCCESS + } + + return command(sender, amount, player) + } + private fun command(sender: CommandSender, amount: Int, target: Player): Int { target.inventory.addItem(defaultKeyProvider.getKey(target, amount)) sender.message(localization["command-give-key", amount, target.name]) diff --git a/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt b/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt index eef4a809..7737821c 100644 --- a/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt +++ b/src/main/kotlin/xyz/atrius/waystones/utility/Item.kt @@ -15,7 +15,7 @@ operator fun ItemMeta.set(key: String, type: PersistentDataType, value .set(key.toKey(), type, value ?: error("Value must be provided!")) fun PlayerInventory.addItemNaturally(original: ItemStack, new: ItemStack) { - val player = holder as Player + val player = holder as? Player ?: return // Add item to inventory if (player.immortal) { addItem(new)