From 76d2adfe6a4b0cb99a3b88adb2108887ab8f8f10 Mon Sep 17 00:00:00 2001 From: The-Epic Date: Sun, 23 Feb 2025 23:58:28 +0000 Subject: [PATCH 1/4] start work --- .gitignore | 6 +- .../java/sh/miles/pineapple/PineappleLib.java | 76 ++++++++++-- .../pineapple/command/AdvancedCommand.java | 108 ++++++++++++++++++ .../sh/miles/pineapple/command/Command.java | 4 +- settings.gradle.kts | 7 +- test-plugin.settings.gradle.kts | 2 + test-plugin/build.gradle.kts | 23 ++++ .../kotlin/sh/miles/testplugin/TestPlugin.kt | 104 +++++++++++++++++ .../src/main/resources/paper-plugin.yml | 7 ++ 9 files changed, 319 insertions(+), 18 deletions(-) create mode 100644 pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java create mode 100644 test-plugin.settings.gradle.kts create mode 100644 test-plugin/build.gradle.kts create mode 100644 test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt create mode 100644 test-plugin/src/main/resources/paper-plugin.yml diff --git a/.gitignore b/.gitignore index aba1ffc2..ca6f814d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,4 @@ bin/ .DS_Store .idea/ -scripts/impl/__pycache__/ - -scripts/ui/__pycache__/ - -scripts/utils/__pycache__/ +run/ \ No newline at end of file diff --git a/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java b/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java index 1ffa39ea..49e86fc3 100644 --- a/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java +++ b/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java @@ -1,8 +1,19 @@ package sh.miles.pineapple; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableBiMap; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.command.brigadier.argument.ArgumentTypes; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import sh.miles.pineapple.command.AdvancedCommand; import sh.miles.pineapple.command.Command; import sh.miles.pineapple.command.CommandLabel; import sh.miles.pineapple.command.internal.PineappleCommandManager; @@ -19,10 +30,14 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.logging.Logger; /** - * The main library class for PineappleLib. That should be loaded using {@link PineappleLib#initialize(Plugin)} + * The main library class for PineappleLib. That should be loaded using {@link PineappleLib#initialize(JavaPlugin)} *

* Provides ease of access to many features of PineappleLib and puts them all into one place. * @@ -32,7 +47,7 @@ public final class PineappleLib { private static PineappleLib instance; - private final Plugin plugin; + private final JavaPlugin plugin; private PineappleNMS nmsProvider; private final ConfigManager configurationManager; private final GuiManager guiManager; @@ -45,7 +60,7 @@ public final class PineappleLib { * @param plugin the plugin * @param useNms whether or not to use NMS */ - private PineappleLib(final Plugin plugin, final boolean useNms) { + private PineappleLib(final JavaPlugin plugin, final boolean useNms) { this.plugin = plugin; if (useNms) { NMSLoader.INSTANCE.activate(plugin.getLogger()); @@ -128,7 +143,7 @@ public static String getVersion() { * @param plugin the plugin * @since 1.0.0-SNAPSHOT */ - public static void initialize(@NotNull final Plugin plugin) { + public static void initialize(@NotNull final JavaPlugin plugin) { instance = new PineappleLib(plugin, false); } @@ -139,7 +154,7 @@ public static void initialize(@NotNull final Plugin plugin) { * @param useNms decides whether or not to use NMS * @since 1.0.0-SNAPSHOT */ - public static void initialize(@NotNull final Plugin plugin, final boolean useNms) { + public static void initialize(@NotNull final JavaPlugin plugin, final boolean useNms) { instance = new PineappleLib(plugin, useNms); } @@ -152,9 +167,52 @@ public static void initialize(@NotNull final Plugin plugin, final boolean useNms public static void registerCommand(@NotNull final Command command) { final CommandLabel label = command.getCommandLabel(); - instance.plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> commands.registrar() - .register(label.getName(), label.getDescription(), label.getAliases(), command) - ); + instance.plugin.registerCommand(label.getName(), label.getDescription(), label.getAliases(), command); + } + + public static void registerCommand(@NotNull final AdvancedCommand advancedCommand) { + instance.plugin.getLifecycleManager() + .registerEventHandler( + LifecycleEvents.COMMANDS, + commands -> commands.registrar().register(convert(advancedCommand).build()) + ); + } + + @SuppressWarnings({"UnstableApiUsage"}) + private static LiteralArgumentBuilder convert(AdvancedCommand advancedCommand) { + var command = Commands.literal(advancedCommand.getCommandLabel().getName()); + + List>> reversedEntries = new ArrayList<>(advancedCommand.getArguments().entrySet()); + Collections.reverse(reversedEntries); + + RequiredArgumentBuilder chainedArgument = null; + for (Map.Entry> entry : reversedEntries) { + if (chainedArgument == null) { + chainedArgument = Commands.argument(entry.getKey(), entry.getValue()).requires(sourceStack -> advancedCommand.canUse(sourceStack.getSender())).executes((context) -> { + advancedCommand.execute(context); + return com.mojang.brigadier.Command.SINGLE_SUCCESS; + }); + } else { + chainedArgument = Commands.argument(entry.getKey(), entry.getValue()).then(chainedArgument); + } + } + + + if (chainedArgument == null) { + command = command.requires(sourceStack -> advancedCommand.canUse(sourceStack.getSender())).executes((context) -> { + advancedCommand.execute(context); + return com.mojang.brigadier.Command.SINGLE_SUCCESS; + }); + } else { + command.then(chainedArgument); + } + + + for (AdvancedCommand subCommand : advancedCommand.getSubcommands()) { + command.then(convert(subCommand)); + } + + return command; } /** @@ -185,7 +243,7 @@ public static void cleanup() { private void loadVersion() { try (final BufferedReader reader = new BufferedReader( - new InputStreamReader(getClass().getResourceAsStream("/pineapple.version"), StandardCharsets.UTF_8) + new InputStreamReader(getClass().getResourceAsStream("/pineapple.version"), StandardCharsets.UTF_8) )) { this.version = reader.readLine(); } catch (IOException ignored) { diff --git a/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java b/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java new file mode 100644 index 00000000..ec08b6f6 --- /dev/null +++ b/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java @@ -0,0 +1,108 @@ +package sh.miles.pineapple.command; + +import com.google.common.base.Preconditions; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AdvancedCommand { + + private final CommandLabel label; + private final CommandSettings.Settings settings; + private final List subcommands; + private final Map> arguments; + + /** + * Creates Command + * + * @param label label + * @param settings settings + * @since 1.0.0-SNAPSHOT + */ + public AdvancedCommand(@NotNull final CommandLabel label, @NotNull final CommandSettings.Settings settings) { + Preconditions.checkNotNull(label); + Preconditions.checkNotNull(settings); + + this.label = label; + this.settings = settings; + this.subcommands = new ArrayList<>(); + this.arguments = new HashMap<>(); + } + + /** + * Creates Command + * + * @param label label + * @since 1.0.0-SNAPSHOT + */ + public AdvancedCommand(@NotNull final CommandLabel label) { + this(label, CommandSettings.DEFAULT_COMMAND_SETTINGS); + } + + public void execute(@NotNull CommandContext<@NotNull CommandSourceStack> context) { + } + + /** + * Register an argument for a command + * Use {@link io.papermc.paper.command.brigadier.argument.ArgumentTypes} for built-in or implement {@link ArgumentType} + * + * @param name argument name + * @param argumentType argument type + * @since 1.0.0-SNAPSHOT + */ + public void registerArgument(@NotNull String name, @NotNull ArgumentType argumentType) { + this.arguments.put(name, argumentType); + } + + /** + * Registers a command under this command. (sub-command) + * + * @param command the command to register + * @since 1.0.0-SNAPSHOT + */ + public void registerSubcommand(@NotNull AdvancedCommand command) { + this.subcommands.add(command); + } + + public boolean canUse(CommandSender sender) { + return sender.hasPermission(label.getPermission()); + } + + public CommandLabel getCommandLabel() { + return label; + } + + public CommandSettings.Settings getSettings() { + return settings; + } + + /** + * Get the subcommands + * + * @return the commands + * @since 1.0.0-SNAPSHOT + */ + @ApiStatus.Internal + public List getSubcommands() { + return new ArrayList<>(subcommands); + } + + /** + * Get the arguments + * + * @return the commands + * @since 1.0.0-SNAPSHOT + */ + @ApiStatus.Internal + public Map> getArguments() { + return new HashMap<>(arguments); + } +} diff --git a/pineapple-core/src/main/java/sh/miles/pineapple/command/Command.java b/pineapple-core/src/main/java/sh/miles/pineapple/command/Command.java index 7ba04dfc..e15f7283 100644 --- a/pineapple-core/src/main/java/sh/miles/pineapple/command/Command.java +++ b/pineapple-core/src/main/java/sh/miles/pineapple/command/Command.java @@ -30,7 +30,7 @@ public class Command implements BasicCommand { protected BiConsumer noArgExecutor = (s, a) -> {}; /** - * Creates SCommand + * Creates Command * * @param label label * @param settings settings @@ -46,7 +46,7 @@ public Command(@NotNull final CommandLabel label, @NotNull final CommandSettings } /** - * Creates SCommand + * Creates Command * * @param label label * @since 1.0.0-SNAPSHOT diff --git a/settings.gradle.kts b/settings.gradle.kts index 5de0bfa9..38b07bdd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,7 +11,6 @@ dependencyResolutionManagement { gradlePluginPortal() maven("https://repo.papermc.io/repository/maven-public/") maven("https://maven.miles.sh/pineapple") - } } @@ -28,4 +27,8 @@ file("pineapple-apis").listFiles()?.forEach { project -> if (project.resolve("build.gradle.kts").exists()) run { include("pineapple-apis:${project.name}") } -} \ No newline at end of file +} + +// Inspired from PaperMC +apply(from = "test-plugin.settings.gradle.kts") +findProject(":test-plugin")?.projectDir = file("test-plugin") \ No newline at end of file diff --git a/test-plugin.settings.gradle.kts b/test-plugin.settings.gradle.kts new file mode 100644 index 00000000..771240b8 --- /dev/null +++ b/test-plugin.settings.gradle.kts @@ -0,0 +1,2 @@ +// Uncomment to enable + include(":test-plugin") \ No newline at end of file diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts new file mode 100644 index 00000000..75a44783 --- /dev/null +++ b/test-plugin/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + kotlin("jvm") + id("com.gradleup.shadow") + id("xyz.jpenilla.run-paper") version "2.3.1" +} + +dependencies { + compileOnly(libs.paper.api) + implementation(project(":pineapple-core")) + implementation(project(":pineapple-common")) + implementation(project(":pineapple-nms:api")) +} + +tasks.runServer { + minecraftVersion("1.21.4") + + workingDir = file("run") + systemProperty("net.kyori.adventure.text.warnWhenLegacyFormattingDetected", true) + + doFirst { + workingDir.mkdirs() + } +} \ No newline at end of file diff --git a/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt b/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt new file mode 100644 index 00000000..970ee5be --- /dev/null +++ b/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt @@ -0,0 +1,104 @@ +package sh.miles.testplugin + +import com.mojang.brigadier.arguments.StringArgumentType +import com.mojang.brigadier.builder.RequiredArgumentBuilder +import com.mojang.brigadier.context.CommandContext +import io.papermc.paper.command.brigadier.CommandSourceStack +import io.papermc.paper.command.brigadier.Commands +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents +import org.bukkit.plugin.java.JavaPlugin +import sh.miles.pineapple.PineappleLib +import sh.miles.pineapple.command.AdvancedCommand +import sh.miles.pineapple.command.CommandLabel + +class TestPlugin: JavaPlugin() { + + @Suppress("UnstableApiUsage") + override fun onEnable() { + PineappleLib.initialize(this) + PineappleLib.registerCommand(TestCommand) + + val arguments = listOf( + Commands.argument("first", StringArgumentType.word()), + Commands.argument("second", StringArgumentType.word()), + ) + + + var chainedArguments: RequiredArgumentBuilder? = null + for (argument in arguments.reversed()) { + chainedArguments = if (chainedArguments == null) { + argument.requires { source -> source.sender.isOp }.executes { ctx -> + val first = StringArgumentType.getString(ctx, "first") + val second = StringArgumentType.getString(ctx, "second") + ctx.source.executor.sendRichMessage( + "Test $first $second" + ) + return@executes 1 + } + } else { + argument.then(chainedArguments) + } + } + + val finalCommand = Commands.literal("egg").then(chainedArguments) + + + + lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS) { commands -> + commands.registrar().register(finalCommand.build()) + commands.registrar().register( + Commands.literal("testcmd").then( + Commands.argument("first", StringArgumentType.word()).then( + Commands.argument("second", StringArgumentType.word()).requires { source -> source.sender.isOp } + .executes { ctx -> + val first = StringArgumentType.getString(ctx, "first") + val second = StringArgumentType.getString(ctx, "second") + ctx.source.executor.sendRichMessage( + "Test $first $second" + ) + return@executes 1 + }) + ).build() + ) + } + + + } +} + +object TestCommand: AdvancedCommand(CommandLabel("testcommand", "test-plugin.command.test")) { + + init { + registerSubcommand(FirstCommand) + registerSubcommand(SecondCommand) + } + + object FirstCommand: AdvancedCommand(CommandLabel("first", super.commandLabel.permission + ".first")) { + + init { + registerArgument("third", StringArgumentType.word()) + registerArgument("fourth", StringArgumentType.word()) + } + + override fun execute(context: CommandContext) { + val sender = context.source.executor!! + + sender.sendRichMessage( + "Executed first command! with ${ + StringArgumentType.getString( + context, "third" + ) + } ${StringArgumentType.getString(context, "fourth")}" + ) + } + + } + + object SecondCommand: AdvancedCommand(CommandLabel("second", super.commandLabel.permission + ".second")) { + override fun execute(context: CommandContext) { + val sender = context.source.executor!! + + sender.sendRichMessage("Executed second command!") + } + } +} diff --git a/test-plugin/src/main/resources/paper-plugin.yml b/test-plugin/src/main/resources/paper-plugin.yml new file mode 100644 index 00000000..31afacd4 --- /dev/null +++ b/test-plugin/src/main/resources/paper-plugin.yml @@ -0,0 +1,7 @@ +name: Test-Plugin +version: "1.0.0" +main: sh.miles.testplugin.TestPlugin +description: Test Plugin +author: Pineapple +api-version: 1.21.4 +defaultPerm: OP \ No newline at end of file From 1d759d14f6e4f758936c9ae3b8746a877192b50f Mon Sep 17 00:00:00 2001 From: The-Epic Date: Mon, 24 Feb 2025 00:04:04 +0000 Subject: [PATCH 2/4] fix comment --- test-plugin.settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-plugin.settings.gradle.kts b/test-plugin.settings.gradle.kts index 771240b8..c83d5207 100644 --- a/test-plugin.settings.gradle.kts +++ b/test-plugin.settings.gradle.kts @@ -1,2 +1,2 @@ // Uncomment to enable - include(":test-plugin") \ No newline at end of file +// include(":test-plugin") \ No newline at end of file From 0fa44081002e108e4f40b606e24c0d87daabff67 Mon Sep 17 00:00:00 2001 From: The-Epic Date: Mon, 24 Feb 2025 00:04:27 +0000 Subject: [PATCH 3/4] ignore testplugin settings --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ca6f814d..2075f581 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ bin/ .DS_Store .idea/ -run/ \ No newline at end of file +run/ +test-plugin.settings.gradle.kts \ No newline at end of file From 3708ec677a87a661a81534715f9b00986df015da Mon Sep 17 00:00:00 2001 From: The-Epic Date: Mon, 24 Feb 2025 13:27:44 +0000 Subject: [PATCH 4/4] retain argument order --- .../src/main/java/sh/miles/pineapple/PineappleLib.java | 3 ++- .../java/sh/miles/pineapple/command/AdvancedCommand.java | 6 +++--- .../src/main/kotlin/sh/miles/testplugin/TestPlugin.kt | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java b/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java index 49e86fc3..bde690e6 100644 --- a/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java +++ b/pineapple-core/src/main/java/sh/miles/pineapple/PineappleLib.java @@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Logger; @@ -182,7 +183,7 @@ public static void registerCommand(@NotNull final AdvancedCommand advancedComman private static LiteralArgumentBuilder convert(AdvancedCommand advancedCommand) { var command = Commands.literal(advancedCommand.getCommandLabel().getName()); - List>> reversedEntries = new ArrayList<>(advancedCommand.getArguments().entrySet()); + List>> reversedEntries = new LinkedList<>(advancedCommand.getArguments().entrySet()); Collections.reverse(reversedEntries); RequiredArgumentBuilder chainedArgument = null; diff --git a/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java b/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java index ec08b6f6..e1fcffa8 100644 --- a/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java +++ b/pineapple-core/src/main/java/sh/miles/pineapple/command/AdvancedCommand.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -34,7 +34,7 @@ public AdvancedCommand(@NotNull final CommandLabel label, @NotNull final Command this.label = label; this.settings = settings; this.subcommands = new ArrayList<>(); - this.arguments = new HashMap<>(); + this.arguments = new LinkedHashMap<>(); } /** @@ -103,6 +103,6 @@ public List getSubcommands() { */ @ApiStatus.Internal public Map> getArguments() { - return new HashMap<>(arguments); + return new LinkedHashMap<>(arguments); } } diff --git a/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt b/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt index 970ee5be..2e22811c 100644 --- a/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt +++ b/test-plugin/src/main/kotlin/sh/miles/testplugin/TestPlugin.kt @@ -30,7 +30,7 @@ class TestPlugin: JavaPlugin() { argument.requires { source -> source.sender.isOp }.executes { ctx -> val first = StringArgumentType.getString(ctx, "first") val second = StringArgumentType.getString(ctx, "second") - ctx.source.executor.sendRichMessage( + ctx.source.sender.sendRichMessage( "Test $first $second" ) return@executes 1 @@ -78,6 +78,7 @@ object TestCommand: AdvancedCommand(CommandLabel("testcommand", "test-plugin.com init { registerArgument("third", StringArgumentType.word()) registerArgument("fourth", StringArgumentType.word()) + registerArgument("fifth", StringArgumentType.word()) } override fun execute(context: CommandContext) {