diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java index 27dbb0857..82f859183 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/BukkitCommandManager.java @@ -33,6 +33,7 @@ public class BukkitCommandManager extends AbstractCommandManager new ReloadCommand(this, plugin), new GetItemCommand(this, plugin), new GiveItemCommand(this, plugin), + new ClearItemCommand(this, plugin), new ItemBrowserPlayerCommand(this, plugin), new ItemBrowserAdminCommand(this, plugin), new SearchRecipePlayerCommand(this, plugin), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ClearItemCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ClearItemCommand.java new file mode 100644 index 000000000..c387f4adf --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ClearItemCommand.java @@ -0,0 +1,98 @@ +package net.momirealms.craftengine.bukkit.plugin.command.feature; + +import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.util.ItemStackUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; +import net.momirealms.craftengine.core.plugin.command.FlagKeys; +import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.Key; +import org.bukkit.NamespacedKey; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.Command; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.bukkit.data.MultiplePlayerSelector; +import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; +import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class ClearItemCommand extends BukkitCommandFeature { + + public ClearItemCommand(CraftEngineCommandManager commandManager, CraftEngine plugin) { + super(commandManager, plugin); + } + + @Override + public Command.Builder assembleCommand(CommandManager manager, Command.Builder builder) { + return builder + .flag(FlagKeys.SILENT_FLAG) + .required("player", MultiplePlayerSelectorParser.multiplePlayerSelectorParser(true)) + .required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(plugin().itemManager().cachedCustomItemSuggestions()); + } + })) + .optional("amount", IntegerParser.integerParser(0)) + .handler(context -> { + MultiplePlayerSelector selector = context.get("player"); + int amount = context.getOrDefault("amount", -1); + NamespacedKey namespacedKey = context.get("id"); + Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value()); + Predicate predicate = nmsStack -> { + Optional id = BukkitItemManager.instance().wrap(ItemStackUtils.asCraftMirror(nmsStack)).customId(); + return id.isPresent() && id.get().equals(itemId); + }; + int totalCount = 0; + Collection players = selector.values(); + for (Player player : players) { + Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player); + Object inventory = FastNMS.INSTANCE.method$Player$getInventory(serverPlayer); + Object inventoryMenu = FastNMS.INSTANCE.field$Player$inventoryMenu(serverPlayer); + totalCount += FastNMS.INSTANCE.method$Inventory$clearOrCountMatchingItems(inventory, predicate, amount, FastNMS.INSTANCE.method$InventoryMenu$getCraftSlots(inventoryMenu)); + FastNMS.INSTANCE.method$AbstractContainerMenu$broadcastChanges(FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer)); + FastNMS.INSTANCE.method$InventoryMenu$slotsChanged(inventoryMenu, inventory); + } + if (totalCount == 0) { + if (players.size() == 1) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_FAILED_SINGLE, Component.text(players.iterator().next().getName())); + } else { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_FAILED_MULTIPLE, Component.text(players.size())); + } + } else { + if (amount == 0) { + if (players.size() == 1) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_TEST_SINGLE, Component.text(totalCount), Component.text(players.iterator().next().getName())); + } else { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_TEST_MULTIPLE, Component.text(totalCount), Component.text(players.size())); + } + } else { + if (players.size() == 1) { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_SUCCESS_SINGLE, Component.text(totalCount), Component.text(players.iterator().next().getName())); + } else { + handleFeedback(context, MessageConstants.COMMAND_ITEM_CLEAR_SUCCESS_MULTIPLE, Component.text(totalCount), Component.text(players.size())); + } + } + } + }); + } + + @Override + public String getFeatureID() { + return "clear_item"; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java index bbebf2a97..2e6425910 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemStackUtils.java @@ -51,4 +51,8 @@ public final class ItemStackUtils { Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); return UniqueIdItem.of(wrappedItem); } + + public static ItemStack asCraftMirror(Object itemStack) { + return FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(itemStack); + } } diff --git a/common-files/src/main/resources/commands.yml b/common-files/src/main/resources/commands.yml index f2ca1e16b..cd3ca9688 100644 --- a/common-files/src/main/resources/commands.yml +++ b/common-files/src/main/resources/commands.yml @@ -43,6 +43,13 @@ give_item: - /craftengine item give - /ce item give +clear_item: + enable: true + permission: ce.command.admin.clear_item + usage: + - /craftengine item clear + - /ce item clear + item_browser_player: enable: true permission: ce.command.player.item_browser diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 7fa0208a1..768fd7644 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -49,6 +49,12 @@ command.item.get.failure.not_exist: "':'':''>" command.item.give.success.multiple: "':'':''>" command.item.give.failure.not_exist: "'>" +command.item.clear.failed.single: "No items were found on player " +command.item.clear.failed.multiple: "No items were found on players" +command.item.clear.success.single: "Removed item(s) from player " +command.item.clear.success.multiple: "Removed item(s) from players" +command.item.clear.test.single: "Found matching item(s) on player " +command.item.clear.test.multiple: "Found matching item(s) on players" command.search_recipe.not_found: "No recipe found for this item" command.search_usage.not_found: "No usage found for this item" command.search_recipe.no_item: "Please hold an item before running this command" diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java index 882d17c90..b488bde08 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/locale/MessageConstants.java @@ -35,4 +35,10 @@ public interface MessageConstants { TranslatableComponent.Builder COMMAND_LOCALE_SET_FAILURE = Component.translatable().key("command.locale.set.failure"); TranslatableComponent.Builder COMMAND_LOCALE_SET_SUCCESS = Component.translatable().key("command.locale.set.success"); TranslatableComponent.Builder COMMAND_LOCALE_UNSET_SUCCESS = Component.translatable().key("command.locale.unset.success"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_SUCCESS_SINGLE = Component.translatable().key("command.item.clear.success.single"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_SUCCESS_MULTIPLE = Component.translatable().key("command.item.clear.success.multiple"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_FAILED_SINGLE = Component.translatable().key("command.item.clear.failed.single"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_FAILED_MULTIPLE = Component.translatable().key("command.item.clear.failed.multiple"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_SINGLE = Component.translatable().key("command.item.clear.test.single"); + TranslatableComponent.Builder COMMAND_ITEM_CLEAR_TEST_MULTIPLE = Component.translatable().key("command.item.clear.test.multiple"); } diff --git a/gradle.properties b/gradle.properties index ff17174fc..7e943f459 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ org.gradle.jvmargs=-Xmx1G # Project settings -project_version=0.0.65.8 -config_version=55 -lang_version=38 +project_version=0.0.65.9 +config_version=56 +lang_version=39 project_group=net.momirealms latest_supported_version=1.21.10 @@ -48,7 +48,7 @@ byte_buddy_version=1.17.8 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.4 -nms_helper_version=1.0.134 +nms_helper_version=1.0.135 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.34.5