GithubHelp home page GithubHelp logo

revxrsal / lamp Goto Github PK

View Code? Open in Web Editor NEW
180.0 6.0 37.0 3.22 MB

A modern annotations-driven commands framework for Java and Kotlin

License: MIT License

Java 98.18% Kotlin 1.82%
library commands command-line minecraft bukkit spigot sponge velocity bungeecord jda

lamp's Introduction

Lamp

Discord JitPack License: MIT Build

Background

Click to expand Building commands has always been a core concept in many applications, and, lots of times, a really boring and cumbersome one to pull off: Having to think of all the possible input from the user, all the mistakes they will make, validating input and then finally executing the actual command logic.

We aren't supposed to mess our hands up with so much of this. We really shouldn't get ourselves dirty with the highly error-prone string manipulation, nor are we supposed to repeat 3 lines of code a thousand times. We also should not be forced to think of all the edge cases and possible output of the user side. Developers should focus on what's important, not what isn't.

Then after all that, we really should make sure our code is clean, maintainable, extendable and flexible.

Building upon this belief, Lamp was born.

Lamp has taken responsibility upon itself to take all the crap of the command creation process: parsing input, validating arguments, auto completions, tokenizing and redirection, and leaves you only to the important part of your job here: the actual command logic.

Through annotations, parameter resolvers, command conditions, permissions, argument validators, cooldowns, dependency injection, auto-completers, Lamp not only makes the command creation process much easier, it also becomes more fun, intuitive and less error prone.

There are many commands frameworks out there, why should I use Lamp?

Glad you asked!

Getting Started

See available versions and modules below.

Maven

pom.xml

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependencies>
    <!-- Required for all platforms -->
    <dependency>
        <groupId>com.github.Revxrsal.Lamp</groupId>
        <artifactId>common</artifactId> 
        <version>[version]</version>
    </dependency>

    <!-- Add your specific platform module here -->
    <dependency>
        <groupId>com.github.Revxrsal.Lamp</groupId>
        <artifactId>[module]</artifactId>
        <version>[version]</version>
    </dependency>  
</dependencies>

Gradle

build.gradle (Groovy)

repositories {
    maven { url = 'https://jitpack.io' }
}

dependencies {
    // Required for all platforms
    implementation 'com.github.Revxrsal.Lamp:common:[version]'

    // Add your specific platform module here
    implementation 'com.github.Revxrsal.Lamp:[module]:[version]'
}

compileJava { // Preserve parameter names in the bytecode
    options.compilerArgs += ["-parameters"]
}

compileKotlin { // optional: if you're using Kotlin
    kotlinOptions.javaParameters = true
}

build.gradle.kts (Kotlin DSL)

repositories {
    maven(url = "https://jitpack.io")
}

dependencies {
    // Required for all platforms
    implementation("com.github.Revxrsal.Lamp:common:[version]")

    // Add your specific platform module here
    implementation("com.github.Revxrsal.Lamp:[module]:[version]")
}

tasks.withType<JavaCompile> { // Preserve parameter names in the bytecode
    options.compilerArgs.add("-parameters")
}

tasks.withType<KotlinJvmCompile> { // optional: if you're using Kotlin
    compilerOptions {
        javaParameters = true
    }
}

Latest stable version

JitPack

Available modules:

Examples

Creating a command handler

public final class BansPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        // Create a command handler here
        BukkitCommandHandler handler = BukkitCommandHandler.create(this);
        handler.register(new BansCommand());
        // (Optional) Register colorful tooltips (Works on 1.13+ only) 
        handler.registerBrigadier();
    }
}

/epicbans ban <player> <days> <reason>

Add -silent to make the ban silent

    @Command("epicbans ban")
    public void banPlayer(
            Player sender,
            @Range(min = 1) long days,
            Player toBan,
            String reason,
            @Switch("silent") boolean silent
    ) {
        if (!silent)
            Bukkit.broadcastMessage(colorize("Player &6" + toBan.getName() + " &fhas been banned!"));
        Date expires = new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(days));
        Bukkit.getBanList(Type.NAME).addBan(toBan.getName(), reason, expires, sender.getName());
    }

Commands to switch game-modes

    @Command({"gmc", "gamemode creative"})
    public void creative(@Default("me") Player sender) {
        sender.setGameMode(GameMode.CREATIVE);
    }

    @Command({"gms", "gamemode survival"})
    public void survival(@Default("me") Player sender) {
        sender.setGameMode(GameMode.SURVIVAL);
    }

    @Command({"gma", "gamemode adventure"})
    public void adventure(@Default("me") Player sender) {
        sender.setGameMode(GameMode.ADVENTURE);
    }

    @Command({"gmsp", "gamemode spectator"})
    public void spectator(@Default("me") Player sender) {
        sender.setGameMode(GameMode.SPECTATOR);
    }

Commands to ping online operators, with 10 minutes delay

    @Command({"opassist", "opa", "helpop"})
    @Cooldown(value = 10, unit = TimeUnit.MINUTES)
    public void requestAssist(Player sender, String query) {
        for (Player player : Bukkit.getOnlinePlayers()) {
            if (player.isOp()) {
                player.playSound(player.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 1f);
                player.sendMessage(colorize("&a" + sender.getName() + " &fneeds help: &b" + query));
            }
        }
    }

Terminate all nearby entities command

    @Command("terminate")
    public void terminate(BukkitCommandActor sender, @Range(min = 1) int radius) {
        int killCount = 0;
        for (Entity target : sender.requirePlayer().getNearbyEntities(radius, radius, radius)) {
            if (target instanceof LivingEntity) {
                ((LivingEntity) target).setHealth(0);
                killCount++;
            }
        }
        sender.reply("&aSuccessfully killed &e" + killCount +" &aplayers!");
    }

With Brigadier:

Radius accepted as it is within range

Radius not accepted

Message players with player selectors

    @Command("pm")
    public void message(Player sender, EntitySelector<Player> players, String message) {
        for (Player player : players) {
            player.sendMessage(sender.getName() + " -> You: " + ChatColor.GOLD + message);
        }
    }

Example selector

Kotlin

Lamp provides first-class support for many Kotlin features, such as:

Default arguments

@Command("sum")
fun sum(
    actor: CommandActor,
    @Optional a: Int = 10,
    @Optional b: Int = Random.nextInt()
) {
    actor.reply("Result: ${a + b}")
}

Suspend functions Note: You must call this to allow Lamp to support suspend functions

myCommandHandler.supportSuspendFunctions()
@Command("countdown")
suspend fun countdown(
    actor: CommandActor,
    @Optional @Range(min = 1.0) value: Int = 20
) {
    var countdown = value
    while (countdown > 0) {
        countdown--
        delay(1000) // Coroutine function
        actor.reply("Countdown: $countdown")
    }
    actor.reply("Countdown finished!")
}

More examples available here

Documentation

Sponsors

If Lamp has made your life significantly easier or you're feeling particularly generous, consider sponsoring the project! It's a great way to support the many hours I've spent maintaining this library and keeps me motivated. Please don't sponsor if you can't afford it.

Donate with PayPal

Huge thanks to those who donated! 😄

If I missed you or you would like to remain anonymous, feel free to shoot me a DM on Discord)

  • Demeng ($50)

lamp's People

Contributors

bivashy avatar creatorfromhell avatar dhugo0022 avatar enoughsdv avatar grabsky avatar growlyx avatar iiahmedyt avatar pixelwarp avatar revxrsal avatar skytasul avatar srblecaute01 avatar tehneon avatar tofpu avatar unicoodev avatar utfunderscore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

lamp's Issues

Need help with registering commands

I know this sounds like a stupid question, but I just don't quite understand how to register commands.
I finally found out that I am supposed to instantiate the BukkitCommandHandler class and not CommandHandler (don't see anywhere in the wiki, where it says that), but I don't understand what exactly I am supposed to parse the #register() method. Could you please provide an example of registering a command, I couldn't find any example like that anywhere
Thanks in advance

"pluginname:command" is considered invalid

Let's say, for example, I register the command "test" in my plugin "testplugin".
I can now use the command "/test" and everything works fine.
However, if I try to type "/testplugin:test", it even shows up in auto-completion, but when executed it states "Invalid command: testplugin:test" (Just in plain text, not as brigadier error or something like that).

So the command seems to get registered correctly even in the mentioned form, but it is still considered invalid in some way

An example from a current project of mine:
image
image
image
(Just "/checktarget" works fine in this case)

`@DefaultFor` requires fixing / `@Default` in v3.1.2 doesn't work properly

Hello,

As described here. the @DefaultFor annotation requires additional polishing and fixing in v3.1.2.

My final code being as follows:

        @Default
        public void corporationInfoDefault(Player p) { corporationInfo(p); }

        @Subcommand("info")
        public void corporationInfo(Player p) {
            wrapper.corporationInfo(p);
            NovaSound.ENTITY_ARROW_HIT_PLAYER.playSuccess(p);
        }

As shown here, firstly, the annotation does not allow sub-commands with command aliases correctly:

image
image

Secondly, when reverting back to the deprecated @Default command, sub-commands fail entirely:

image

I've downgraded to v3.1.1 until @Revxrsal has gotten back and fixed these issues in the next version.
Thanks

Buffer overrun error

Hey, I've encountered another error:

[11:40:46 INFO]: JasperTheDev issued server command: /cmd
[11:41:12 INFO]: [Not Secure] <JasperTheDev> test
[11:41:15 INFO]: JasperTheDev issued server command: /cmd\
[11:41:15 ERROR]: Could not pass event UnknownCommandEvent to ExamplePlugin v1.0-SNAPSHOT
revxrsal.commands.exception.ArgumentParseException: Buffer overrun while parsing args
	at revxrsal.commands.util.QuotedStringTokenizer$TokenizerState.createException(QuotedStringTokenizer.java:174) ~[ExamplePlugin-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer$TokenizerState.next(QuotedStringTokenizer.java:168) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parseEscape(QuotedStringTokenizer.java:142) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parseUnquotedString(QuotedStringTokenizer.java:133) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.nextArg(QuotedStringTokenizer.java:97) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parse(QuotedStringTokenizer.java:66) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.command.ArgumentStack.parse(ArgumentStack.java:113) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.bukkit.brigadier.PaperCommodore$UnknownCommandListener.onUnknownCommand(PaperCommodore.java:74) ~[MineKeoFPS-1.0-SNAPSHOT-all.jar:?]
	at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor50.execute(Unknown Source) ~[?:?]
	at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-292]
	at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-292]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-292]
	at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:374) ~[?:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:314) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.performChatCommand(ServerGamePacketListenerImpl.java:2208) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleChatCommand$19(ServerGamePacketListenerImpl.java:2168) ~[?:?]
	at net.minecraft.util.thread.BlockableEventLoop.lambda$submitAsync$0(BlockableEventLoop.java:59) ~[?:?]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[?:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:153) ~[?:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[?:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1324) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:193) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:126) ~[?:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1301) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1294) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:136) ~[?:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1272) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1160) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:315) ~[paper-1.20.2.jar:git-Paper-292]
	at java.lang.Thread.run(Thread.java:842) ~[?:?]
[11:41:15 ERROR]: Could not pass event UnknownCommandEvent to AnotherPlugin v1.0
revxrsal.commands.exception.ArgumentParseException: Buffer overrun while parsing args
	at revxrsal.commands.util.QuotedStringTokenizer$TokenizerState.createException(QuotedStringTokenizer.java:174) ~[AnotherPlugin-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer$TokenizerState.next(QuotedStringTokenizer.java:168) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parseEscape(QuotedStringTokenizer.java:142) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parseUnquotedString(QuotedStringTokenizer.java:133) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.nextArg(QuotedStringTokenizer.java:97) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.util.QuotedStringTokenizer.parse(QuotedStringTokenizer.java:66) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.command.ArgumentStack.parse(ArgumentStack.java:113) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at revxrsal.commands.bukkit.brigadier.PaperCommodore$UnknownCommandListener.onUnknownCommand(PaperCommodore.java:74) ~[Cosmetics-1.0-SNAPSHOT-all.jar:?]
	at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor52.execute(Unknown Source) ~[?:?]
	at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-292]
	at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-292]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-292]
	at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:374) ~[?:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:314) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.performChatCommand(ServerGamePacketListenerImpl.java:2208) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleChatCommand$19(ServerGamePacketListenerImpl.java:2168) ~[?:?]
	at net.minecraft.util.thread.BlockableEventLoop.lambda$submitAsync$0(BlockableEventLoop.java:59) ~[?:?]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[?:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:153) ~[?:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[?:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1324) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:193) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:126) ~[?:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1301) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1294) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:136) ~[?:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1272) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1160) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:315) ~[paper-1.20.2.jar:git-Paper-292]
	at java.lang.Thread.run(Thread.java:842) ~[?:?]

Both ExamplePlugin and AnotherPlugin use Lamp. They both error.

feat: minecraft brigadier argument types

Lamp currently supports arguments provided by Brigadier library directly. There are, however more Minecraft-specific arguments included in game internals. All of them are handled by the client to support completions etc. (see wiki)

They can be accessed with a bit of reflection or NMS on supported platforms. Do note there is a Paper PR (PaperMC/Paper#6695) open to expose them directly to the API which may, or may not be merged in the future.

Notes

  • Some existing arguments would need to be handled differently to mimic their brigadier parser behavior.
    • Lamp uses StringArgumentType for String which expects validation (see this)
    • EntitySelector<T> should refer to internal minecraft:entity type to support rich client-side completions.
  • Supporting some arguments (eg. minecraft:vec2 and vec3) could be a bit hacky and would most likely require internal changes.

Thoughts

Since that have more side effects than it sounds, I think that addition of BrigadierCommandHandler (or similar) is necessary. This would keep current users unaffected by these changes and also make brigadier easier to maintain.

Sponge

I know nothing about Sponge and can't really suggest anything in that regards. I can only say that since all (minecraft) platforms can use Brigadier, maybe a new module structure is needed. (example)

MinecraftCommandHandler<T> extends CommandHandler<T> { ... }
BrigadierCommandHandler<T> extends MinecraftCommandHandler<T> { ... }

MinecraftCommandHandler<Bukkit> handler = ... // for simple commands
BrigadierCommandHandler<Bukkit> handler = ... // for brigadier commands

MinecraftCommandHandler<Sponge> handler = ... // for simple commands
BrigadierCommandHandler<Sponge> handler = ... // for brigadier commands

default argument type resolvers do not make sense to me.

each default arg type bound like this:
image
but the type does not do anything at all in the bind method:
image
and the parameter's class type hasn't check in the argument resolver too:
image
and looks like it does support nullable too:
image
so i've made the string resolver like this:

public interface CommandFactory {
  @Inject
  @NotNull
  @Provides
  @Singleton
  static BukkitCommandHandler commandManager(@NotNull final Plugin plugin) {
    final var handler = BukkitCommandHandler.create(plugin);
    handler.registerBrigadier();
    handler.enableAdventure();
    handler
      .getBrigadier()
      .orElseThrow()
      .registerArgumentTypeResolver(
        0,
        parameter -> {
          if (parameter.getType() != String.class) {
            return null;
          }
          if (parameter.getType().isAnnotationPresent(Quoted.class)) {
            return StringArgumentType.string();
          }
          if (parameter.consumesAllString()) {
            return StringArgumentType.greedyString();
          }
          return StringArgumentType.word();
        }
      );
    return handler;
  }
}

am i wrong with the returning null if the parameter is not a string? because looks like if it does not find any resolver, returns string already so it should check the parameter type first.

[Bug] Default annotation conflicts with subcommand

When the Default and Subcommand annotation are used together on the some method, it generates some weird conflicts.

  1. The Default annotations routing functionality of the root/parent command is removed
  2. The Default annotations "hidden" nature is still applied and hides the Subcommands aliases from the autocompleter

Properly functioning example:

@Command("default")
class DefaultCommand
{

	@Default
	fun default(sender: CommandSender, @Default("1") page: Int)
	{
		sender.sendMessage("Default subcommand, page: $page.")
	}

	@Subcommand("test")
	fun test(sender: CommandSender)
	{
		sender.sendMessage("Test subcommand!")
	}

}

When issuing commands:
/default -> DefaultCommand#default
/default test -> DefaultCommand#test

Conflicting version:

@Command("default")
class DefaultCommand
{

	@Default
	@Subcommand("help")
	fun default(sender: CommandSender, @Default("1") page: Int)
	{
		sender.sendMessage("Default subcommand, page: $page.")
	}
	}

	@Subcommand("test")
	fun test(sender: CommandSender)
	{
		sender.sendMessage("Test subcommand!")
	}

}

When issuing commands:
/default -> Errors, subcommand must be specified
/default help -> DefaultCommand#default
/default test -> DefaultCommand#test

What I would expect it to do:
/default -> DefaultCommand#default
/default help -> DefaultCommand#default
/default test -> DefaultCommand#test

With this conflicting version, the autocompleter also doesn't show that the help subcommand exists either and only provides the user with the test subcommand.

NoSuchMethodError exception thrown

Hi! first, of all, I'd like to thank you for creating this framework, it's incredibly useful to me & (pretty sure) others!

Back to the point, the NoSuchMethodError exception is being thrown when attempting to access the player's locale (Player#getLocale) on 1.8.8. Such a method doesn't exist on 1.8.8 unfortunately.

java.lang.NoSuchMethodError: 'java.lang.String org.bukkit.entity.Player.getLocale()'

[02:10:10 WARN]:        at revxrsal.commands.bukkit.core.BukkitActor.getLocale(BukkitActor.java:90)

[02:10:10 WARN]:        at revxrsal.commands.command.CommandActor.errorLocalized(CommandActor.java:106)

[02:10:10 WARN]:        at revxrsal.commands.exception.DefaultExceptionHandler.missingArgument(DefaultExceptionHandler.java:25)

You can avoid the exception by adding a check that ensures that the server version is on a certain version (1.12.2 & higher) before attempting to execute the method.

command error if there's a colon in a string argument

After upgrading my server to Lamp 3c22956 (from Lamp 3.1.3), I noticed one of my commands, that frequently takes inputs with multiple colons in it, no longer works.

Here's the source of the offending command, as well as examples of before (working) and after (broken since upgrading):
image
image
image

I haven't found a way to fix this on my end as of yet.

EntitySelector not sending tabcompletion

Earlier today I updated Lamp from 3.1.5 to the latest commit (b61c1dd) to fix the "Unknown command issue". After some testing I found out that the EntitySelector no longer sends tabcompletions.

Buffer Overrun When Ending Command With "\"

Description

When ending any command with a backslash \ while using Lamp as my command framework, an ArgumentParseException is thrown with a message indicating a buffer overrun while parsing arguments.

Environment

  • Server: Paper 1.20.4
  • Lamp Version: 3.1.8

Steps to Reproduce

  1. Execute a command that ends with a backslash \.
  2. Observe the error in the server console.

Expected Behavior

Commands ending with a backslash should be processed without causing any errors.

Actual Behavior

An error is thrown in the console with the following stack trace: https://gist.github.com/DoudGeorges/a599c49d3247d0e140ed5a6e1cde2c79

Args in command (not at the ending)

Hey, I've been playing with Lamp for a while and I am trying something, but I can't figure out how.

Is ist possible to set args at a specific position in the (sub)command?
Like "user <name> changePassword <password>" and not having all args at the end?

DiscordSRV Errors

Hello,
One of my users using DiscordSRV is reporting some errors. I have the library shaded, so there shouldn't be any conflicts. Do you think there's an issue on your end or DiscordSRV's end?

[13:58:15 ERROR]: Could not pass event UnknownCommandEvent to StarCosmetics v1.3.2
java.util.NoSuchElementException: null
at java.util.LinkedList.getFirst(Unknown Source) ~[?:?]
at me.gamercoder215.starcosmetics.shaded.lamp.bukkit.brigadier.PaperCommodore$UnknownCommandListener.onUnknownCommand(PaperCommodore.java:75) ~[starcosmetics-1.3.2.jar:?]
at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor91.execute(Unknown Source) ~[?:?]
at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.4-R0.1-SNAPSHOT.jar:?]
at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.4-R0.1-SNAPSHOT.jar:git-Paper-409]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.4-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.4.jar:git-Paper-409]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.4.jar:git-Paper-409]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.4-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.v1_20_R3.CraftServer.dispatchCommand(CraftServer.java:995) ~[paper-1.20.4.jar:git-Paper-409]
at github.scarsz.discordsrv.listeners.DiscordConsoleListener.lambda$onGuildMessageReceived$0(DiscordConsoleListener.java:91) ~[DiscordSRV-Build-1.27.0.jar:?]
at github.scarsz.discordsrv.util.SchedulerUtil.lambda$runTask$0(SchedulerUtil.java:86) ~[DiscordSRV-Build-1.27.0.jar:?]
at io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler$GlobalScheduledTask.run(FoliaGlobalRegionScheduler.java:179) ~[paper-1.20.4.jar:?]
at io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler.tick(FoliaGlobalRegionScheduler.java:37) ~[paper-1.20.4.jar:?]
at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1639) ~[paper-1.20.4.jar:git-Paper-409]
at net.minecraft.server.dedicated.DedicatedServer.tickChildren(DedicatedServer.java:446) ~[paper-1.20.4.jar:git-Paper-409]
at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1515) ~[paper-1.20.4.jar:git-Paper-409]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1216) ~[paper-1.20.4.jar:git-Paper-409]
at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:319) ~[paper-1.20.4.jar:git-Paper-409]
at java.lang.Thread.run(Unknown Source) ~[?:?]
[13:58:15 INFO]: That command is not registered in our database.

Parent Command & Standalone Command simultaneously

Hey! Is it possible to have a command under a parent, and also be standalone simultaneously? If that doesn't make sense to you, here; let me show you a demonstration of the idea:

@Command("parent")
public final class ExampleCommand {
    
    @Default // /parent
    public String defaultCommand() {
        return "default command";
    }
    
    @Subcommand("sub") // /parent sub
    public String subCommand() {
        return "sub command";
    }
    
    @Command("test") // /test
    @Subcommand("test") // /parent test
    public String subCommand2() {
        return "test command";
    }
}

If so, could you please show me how I could achieve such a thing? Thank you!

[3.1.0] java.lang.IncompatibleClassChangeError

Details:

  • Lamp: 3.1.0
  • Minecraft Server Version: 1.8.8
  • Java 17

Error:

[13:53:15 WARN]: java.lang.IncompatibleClassChangeError: Class com.google.common.base.Suppliers$MemoizingSupplier does not implement the requested interface java.util.function.Supplier
[13:53:15 WARN]: 	at revxrsal.commands.bukkit.core.BukkitActor.getLocale(BukkitActor.java:119)
[13:53:15 WARN]: 	at revxrsal.commands.command.CommandActor.errorLocalized(CommandActor.java:106)
[13:53:15 WARN]: 	at revxrsal.commands.exception.DefaultExceptionHandler.commandInvocation(DefaultExceptionHandler.java:60)

Notes:

I tested with 1.19 and the exception handle worked fine, The stacktrace is printed in the console instead of the error above.
It didn't happen in 3.0.71.

feat: `@Length` Annotation for String Parameters

Hello,

I am proposing to add a @Length annotation that would create a minimum/maximum length for Strings. I'm pretty sure this is natively supported by brigadier, but I'm not sure. I couldn't find an existing annotation that exists for this purpose, so I'm proposing a new one.

It would look like this:

public @interface Length {
    
    int min() default 0;
    
    int max() default Integer.MAX_VALUE; // Something like that
}

Could be used in scenarios like this:

@Command("nick")
@Description("Set a Player's Nickname")
public void nickPlayer(Player player, @Length(min = 3, max = 32) String name) {
    player.setDisplayName(name);
}

Also: I would recommend creating a build script that automatically updates the JavaDocs instead of manually copying files.

Sub-commands do not ignore case

While base commands ignore capitalization, sub-commands do not seem to do so. In addition, Lamp seems to automatically convert all sub-commands to lowercase, so you essentially cannot have any sub-commands with uppercase characters. This was tested in Bukkit.

public class TestCmd {

  // "/test capitalization" works
  // "/test CAPITALization" does not work
  // "/test capiTALization" does not work
  @Command("test CAPITALization")
  public void runTest(CommandActor actor) {
    actor.reply("Hello world!");
  }
}

Fabric

Hello,

I was looking over this project, which is really awesome, and was wondering if the Brigadier module allows for Fabric MC support.

Thanks in advance,

Change Messages

Would be useful to change what messages say, like 'no permission' you could change it to what you want.

Varargs Parameter

Add parameter types that parses variables such as arrays such as Player[]. This should come with an annotation for the delimiter such as @delimiter(","), or just parse it based off space separated values by default.

feat: Flag and Switch aliases

Hello,

A neat suggestion I think would be nice for Lamp is the option for @Flag and @Switch annotations to support aliases.
I'm unsure if this is natively supported by brigadier since I've never worked with commands before, but it would be cool to use.

Perhaps the inputted array could override all of the names for the switch/flag, or add on upon the inputted parameter name (that could be an option in the CommandHandler).
The original message is available on the Lamp Discord.

Something like this:

public void send(Player sender, @Flag({"sendtype", "sendingtype", "st"}) SendType type, @Switch({"broadcast", "sendall"}) boolean sendAll) {
   // ...
}

@SkytAsul (their original message) also recommended the option to mix flags and/or switches together, as in some Unix commands, such as:

rm -r -f
# Becomes...
rm -rf

Thanks!

Custom exception when @Flag value is not specified

When we don't specify a flag value, an IndexOutOfBoundsException is thrown.

Can we have custom exception (For example FlagValueNotAssignedException) for this moment?

Command: test flag -test

Code:

import revxrsal.commands.annotation.Command;
import revxrsal.commands.annotation.Flag;
import revxrsal.commands.annotation.Subcommand;

@Command("test")
public class TestCommand {
	
	@Subcommand("flag")
	public void onDefault(@Flag("test") String test) {
		
	}
	
}

Stacktrace:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
  at java.util.LinkedList.checkElementIndex(Unknown Source)
  at java.util.LinkedList.remove(Unknown Source)
  at revxrsal.commands.core.BaseCommandDispatcher.handleFlag(BaseCommandDispatcher.java:186)
  at revxrsal.commands.core.BaseCommandDispatcher.getMethodArguments(BaseCommandDispatcher.java:105)
  at revxrsal.commands.core.BaseCommandDispatcher.execute(BaseCommandDispatcher.java:82)
  at revxrsal.commands.core.BaseCommandDispatcher.searchCategory(BaseCommandDispatcher.java:61)
  at revxrsal.commands.core.BaseCommandDispatcher.eval(BaseCommandDispatcher.java:44)
  at revxrsal.commands.core.BaseCommandHandler.dispatch(BaseCommandHandler.java:440)

Sponge 8 Support.

As the title suggests. Sponge 8 has broken many things from Sponge 7.

Command Suggestion Error

As of v1.4 there appears to be an issue with parent commands with suggesting subcommands out of the gate. With the example code the when getting suggestions to prompt when inputting "/testcommand " the error will occur. As soon as there is further input, i.e "/testcommand sub", suggestions will be provided.

Example command:

import org.bukkit.command.CommandSender
import revxrsal.commands.annotation.Command
import revxrsal.commands.annotation.Subcommand

@Command("testcommand")
class TestCommand
{

	@Subcommand("subcommand1")
	fun subCommand1(sender: CommandSender)
	{
		sender.sendMessage("1")
	}

	@Subcommand("subcommand2")
	fun subCommand2(sender: CommandSender)
	{
		sender.sendMessage("2")
	}

}

Error log:

org.bukkit.command.CommandException: Unhandled exception during tab completion for command '/testcommand ' in plugin LampTest v1.0-SNAPSHOT
	at org.bukkit.command.PluginCommand.tabComplete(PluginCommand.java:150) ~[patched_1.17.1.jar:git-Paper-302]
	at org.bukkit.command.Command.tabComplete(Command.java:93) ~[patched_1.17.1.jar:git-Paper-302]
	at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:244) ~[patched_1.17.1.jar:git-Paper-302]
	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.tabCompleteCommand(CraftServer.java:2136) ~[patched_1.17.1.jar:git-Paper-302]
	at org.bukkit.craftbukkit.v1_17_R1.CraftServer.tabComplete(CraftServer.java:2108) ~[patched_1.17.1.jar:git-Paper-302]
	at org.bukkit.craftbukkit.v1_17_R1.command.BukkitCommandWrapper.getSuggestions(BukkitCommandWrapper.java:58) ~[patched_1.17.1.jar:git-Paper-302]
	at com.mojang.brigadier.tree.ArgumentCommandNode.listSuggestions(ArgumentCommandNode.java:71) ~[patched_1.17.1.jar:git-Paper-302]
	at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:598) ~[patched_1.17.1.jar:git-Paper-302]
	at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:580) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleCustomCommandSuggestions$5(ServerGamePacketListenerImpl.java:819) ~[app:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:149) ~[app:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:23) ~[app:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1418) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.server.MinecraftServer.shouldRun(MinecraftServer.java:192) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:122) ~[app:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1396) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1389) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:132) ~[app:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1367) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1278) ~[patched_1.17.1.jar:git-Paper-302]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:319) ~[patched_1.17.1.jar:git-Paper-302]
	at java.lang.Thread.run(Thread.java:831) ~[?:?]
Caused by: java.lang.IllegalStateException: Path cannot be empty!
	at revxrsal.commands.util.Preconditions.notEmpty(Preconditions.java:17) ~[LampTest.jar:?]
	at revxrsal.commands.core.CommandPath.get(CommandPath.java:40) ~[LampTest.jar:?]
	at revxrsal.commands.core.BaseAutoCompleter.complete(BaseAutoCompleter.java:76) ~[LampTest.jar:?]
	at revxrsal.commands.bukkit.core.BukkitCommandExecutor.onTabComplete(BukkitCommandExecutor.java:41) ~[LampTest.jar:?]
	at org.bukkit.command.PluginCommand.tabComplete(PluginCommand.java:138) ~[patched_1.17.1.jar:git-Paper-302]
	... 22 more

[Enhancement] Auto Completion Configs

Hi, I have been playing around with Lamp for a few days now, and the fact that Auto Completion Configs are missing, is quite limiting for my use cases (e.g. showing a list of player with a specific permission), as it would be unreasonnable to register a different SuggestionProvider for each variation.

How it could be implemented

@Command("example")
@AutoComplete("@completion:key1=value1,key2=42")
public void command(...) {
    ...
}
commandHandler.getAutoCompleter().registerSuggestion("completion", (args, sender, command, configs) -> {
    if (configs.has("key1")) {
        String value = configs.getAsString("key1");
        ...
    } else if (configs.has("key2") {
        int value = configs.getAsInt("key2");
        ...
    }
}

NoSuchElement exception

Hey, I've sadly encountered another issue in Lamp.. It happens when I execute /, here's the error:

[11:46:43 INFO]: [Not Secure] <JasperTheDev> t
[11:46:48 INFO]: JasperTheDev issued server command: /
[11:46:48 ERROR]: Could not pass event UnknownCommandEvent to ExamplePlugin v1.0-SNAPSHOT
java.util.NoSuchElementException: null
	at java.util.LinkedList.getFirst(LinkedList.java:248) ~[?:?]
	at revxrsal.commands.bukkit.brigadier.PaperCommodore$UnknownCommandListener.onUnknownCommand(PaperCommodore.java:75) ~[ExamplePlugin-1.0-SNAPSHOT-all.jar:?]
	at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor50.execute(Unknown Source) ~[?:?]
	at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-292]
	at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-292]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-292]
	at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:374) ~[?:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:314) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.performChatCommand(ServerGamePacketListenerImpl.java:2208) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleChatCommand$19(ServerGamePacketListenerImpl.java:2168) ~[?:?]
	at net.minecraft.util.thread.BlockableEventLoop.lambda$submitAsync$0(BlockableEventLoop.java:59) ~[?:?]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[?:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:153) ~[?:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[?:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1324) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:193) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:126) ~[?:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1301) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1294) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:136) ~[?:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1272) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1160) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:315) ~[paper-1.20.2.jar:git-Paper-292]
	at java.lang.Thread.run(Thread.java:842) ~[?:?]
[11:46:48 ERROR]: Could not pass event UnknownCommandEvent to AnotherPlugin v1.0
java.util.NoSuchElementException: null
	at java.util.LinkedList.getFirst(LinkedList.java:248) ~[?:?]
	at revxrsal.commands.bukkit.brigadier.PaperCommodore$UnknownCommandListener.onUnknownCommand(PaperCommodore.java:75) ~[AnotherPlugin-1.0-SNAPSHOT-all.jar:?]
	at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor52.execute(Unknown Source) ~[?:?]
	at org.bukkit.plugin.EventExecutor$2.execute(EventExecutor.java:77) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:git-Paper-292]
	at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54) ~[paper-1.20.2.jar:git-Paper-292]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126) ~[paper-1.20.2.jar:git-Paper-292]
	at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615) ~[paper-api-1.20.2-R0.1-SNAPSHOT.jar:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:374) ~[?:?]
	at net.minecraft.commands.Commands.performCommand(Commands.java:314) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.performChatCommand(ServerGamePacketListenerImpl.java:2208) ~[?:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleChatCommand$19(ServerGamePacketListenerImpl.java:2168) ~[?:?]
	at net.minecraft.util.thread.BlockableEventLoop.lambda$submitAsync$0(BlockableEventLoop.java:59) ~[?:?]
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[?:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:153) ~[?:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[?:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1324) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:193) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:126) ~[?:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1301) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1294) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:136) ~[?:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1272) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1160) ~[paper-1.20.2.jar:git-Paper-292]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:315) ~[paper-1.20.2.jar:git-Paper-292]
	at java.lang.Thread.run(Thread.java:842) ~[?:?]

Fix wiki Page with changes according to v3.1.2

Hello,
I noticed that you forgot to update the Lamp wiki with changes reflected in v3.1.2.

The first and third examples here, the second and fourth example here, the second example here, the third example here and the second and third example here use the old @Default functionality.

I would also recommend creating a wiki page explaining @DefaultFor if you haven't done so already.

Thanks 😄

Paper 1.19.2 brigadier no argument command issue

Hey,

I've encountered an issue with paper brigadier using latest Lamp version (from latest commit) on Paper 1.19.2.

Commands with multiple arguments (including optional ones) work as intended. But when I create a command with no arguments, brigadier expects an argument (which shouldn't be happening).

Code example:

@Command("testcmd")
public void execute(Player player) { ... }

Result in-game:
In-game screenshot

I should note that the same issue is with subcommands - if subcommand doesn't have arguments, same issue occurs.

[Feature] Custom message types in Locale/Translator

Description:
I've always wanted to utilize the Locale feature to offer users multi-language messages. However, there's a primary issue: it doesn't support custom message types, such as Component. Due to this limitation, we cannot send clickable/hoverable messages to our BukkitCommandActor.

Solution:
We could create another interface that extends LocaleReader called LocaleReaderTransmitter, which would implement the LocaleReaderTransmitter#sendToActor(CommandActor) method.

Alternatives I've Considered:
I've tried using CommandExceptionAdapter and BukkitExceptionAdapter, but I'm not happy with that approach. That's because replyLocalized or errorLocalized still utilize the LocaleReader that only supports strings.

Missing Argument = Unknown Command?

I have a command class (blockregen region) with a subcommand (create) and a parameter (regenType). When a player runs the command with the parameter, tabcompleteion and suggestions work just fine. However when they run the command with nothing in the argument, it is supposed to say "You must specify a value for..." but it does not. Instead it just says "Unknown command. Type /help...".
Oddly enough, this works fine in console... there it tells me that I need to specify a value, and then proceeds to tell me that I need to be a player.

Allow invalid usernames (GeyserMC/Floodgate)

An user of my plugin using Lamp on a GeyserMC+Floodgate server is having this issue while trying to execute a command with a player selector:

He sent me his Floodgate config:

# Floodgate prepends a prefix to bedrock usernames to avoid conflicts
# However, certain conflicts can cause issues with some plugins so this prefix is configurable using the property below
# It is recommended to use a prefix that does not contain alphanumerical to avoid the possibility of duplicate usernames.
username-prefix: '*'

It seems like Lamp recognizes the star as an incorrect username character and completely blocks player selector from there.
Is it possible to fix this, or at least have an option somewhere to allow "unsafe" usernames?
Some plugins do have such options: LuckPerms have a allow-invalid-usernames boolean config option, and ChestChop have a VALID_PLAYERNAME_REGEXP regex option.

get missing permission from NoPermissionException handler

Hi, i have a question. Is it possible to get missing permission from NoPermissionException handler? if so how to do it?

my code:
commandHandler.registerExceptionHandler(NoPermissionException.class, (actor, ex) -> {
TextBuilder.builder()
.text("&7You dont have permission to execute this command! &7(&c{PERM}&7)")
.placeholder("{PERM}", I WANT TO GET MISSING PERMISSION HERE )
.send(((BukkitCommandActor) actor).getSender());
});

Parent commands do not allow non-subcommand overload

i.e. if I have the following commands:

mycom
mycom list
mycom add

The only command that shows up is mycom.
(Each is its own method, and the path is specified in full via @Command)
I am currently working on a plugin that would benefit from this functionality, so it would be best if Lamp supported it.

Support Mojang Brigadier

This issue is to track the progress of supporting Mojang's Brigadier command framework, with the ability to convert components of Lamp to Brigadier's command types, as well as suggestions and tooltips.

Checklist:

  • Convert commands to Brigadier's components
  • Provide mechanism for hooking it into Bukkit, Velocity, and any other platform with built-in Brigadier support
  • Map command parameters to their appropriate Brigadier counterpart, for example an int parameter would become an IntArgumentType.
  • Add @Range annotation to allow leveraging Brigader's numerical validation.
  • Respect strings that come at the end ("greedy strings")
  • Support flags and switches out of the box.

NoSuchElementException when dispatching command with ArgumentStack with empty String

Error occurs with jda module. When user sends picture, or emote MessageReceivedEvent occurs, and MessageReceivedEvent#getMessage#getContentRaw returns empty string.

java.util.NoSuchElementException
at java.util.LinkedList.getFirst(LinkedList.java:244)
at revxrsal.commands.core.BaseCommandDispatcher.eval([BaseCommandDispatcher.java:33](https://github.com/Revxrsal/Lamp/blob/5d00ebe8ec30e2f5f24db5d57717fc0a682c8eb2/common/src/main/java/revxrsal/commands/core/BaseCommandDispatcher.java#L33))
at revxrsal.commands.core.BaseCommandHandler.dispatch([BaseCommandHandler.java:519](https://github.com/Revxrsal/Lamp/blob/5d00ebe8ec30e2f5f24db5d57717fc0a682c8eb2/common/src/main/java/revxrsal/commands/core/BaseCommandHandler.java#L519))

I think it occurs because BaseCommandHandler#parseArguments returns ArgumentStack.empty() when string empty.

So if we add something like this to the BaseCommandDispatcher#eval:

if(arguments.isEmpty()) throw new InvalidCommandException("", "");

Everything will be fine

Set a description for a parent command.

Hi,

I create a command with subcommands with @command, @subcommand, @default.
Something like that

@Command("test")
@Description("Description") // I tried here
public class TestCommand {

    @Default
    @Description("Description") // and here
    public void onRoot(BukkitCommandActor actor) {

    }

    @Subcommand("test reload")
    @CommandPermission("test.command.reload")
    public void onTest(BukkitCommandActor actor) {
        //plugin.onDisable();
        //plugin.onEnable();
        actor.reply(plugin.getPrefix() + "§cThe plugin has been reloaded successfully");
    }
}

But the description if I execute /help test didn't changed. It shows "test: " without an description. How can I change the plugin command description in the help page? Maybe someone can help me.

Best regards,
Ricardo

Issue with AutoCompleter

I'm having an issue with the AutoCompleter but I am unable to determine what exactly is going wrong from the Stacktrace

[23:24:17 ERROR]: Exception when raffel080108 attempted to tab complete spawnbossentities:spawnbossentity 
org.bukkit.command.CommandException: Unhandled exception during tab completion for command '/spawnbossentities:spawnbossentity ' in plugin SpawnBossEntities v1.0
	at org.bukkit.command.PluginCommand.tabComplete(PluginCommand.java:150) ~[purpur-api-1.19.3-R0.1-SNAPSHOT.jar:?]
	at org.bukkit.command.Command.tabComplete(Command.java:93) ~[purpur-api-1.19.3-R0.1-SNAPSHOT.jar:?]
	at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:253) ~[purpur-api-1.19.3-R0.1-SNAPSHOT.jar:?]
	at org.bukkit.craftbukkit.v1_19_R2.CraftServer.tabCompleteCommand(CraftServer.java:2379) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at org.bukkit.craftbukkit.v1_19_R2.CraftServer.tabComplete(CraftServer.java:2351) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at org.bukkit.craftbukkit.v1_19_R2.command.BukkitCommandWrapper.getSuggestions(BukkitCommandWrapper.java:74) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at com.mojang.brigadier.tree.ArgumentCommandNode.listSuggestions(ArgumentCommandNode.java:71) ~[brigadier-1.0.18.jar:git-Purpur-1905]
	at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:601) ~[purpur-1.19.3.jar:?]
	at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:581) ~[purpur-1.19.3.jar:?]
	at net.minecraft.server.network.ServerGamePacketListenerImpl.lambda$handleCustomCommandSuggestions$5(ServerGamePacketListenerImpl.java:939) ~[?:?]
	at net.minecraft.server.TickTask.run(TickTask.java:18) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.util.thread.BlockableEventLoop.doRunTask(BlockableEventLoop.java:153) ~[?:?]
	at net.minecraft.util.thread.ReentrantBlockableEventLoop.doRunTask(ReentrantBlockableEventLoop.java:24) ~[?:?]
	at net.minecraft.server.MinecraftServer.doRunTask(MinecraftServer.java:1368) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:197) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.util.thread.BlockableEventLoop.pollTask(BlockableEventLoop.java:126) ~[?:?]
	at net.minecraft.server.MinecraftServer.pollTaskInternal(MinecraftServer.java:1345) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.server.MinecraftServer.pollTask(MinecraftServer.java:1338) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.util.thread.BlockableEventLoop.managedBlock(BlockableEventLoop.java:136) ~[?:?]
	at net.minecraft.server.MinecraftServer.waitUntilNextTick(MinecraftServer.java:1316) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1204) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:321) ~[purpur-1.19.3.jar:git-Purpur-1905]
	at java.lang.Thread.run(Thread.java:833) ~[?:?]
Caused by: java.lang.LinkageError: loader constraint violation: when resolving method 'void revxrsal.commands.bukkit.core.BukkitActor.<init>(org.bukkit.command.CommandSender, revxrsal.commands.CommandHandler)' the class loader 'spawnbossentities-1.0.jar' @497a00ff of the current class, revxrsal/commands/bukkit/core/BukkitCommandExecutor, and the class loader 'customachievementgui-1.0.jar' @7f7069cd for the method's defining class, revxrsal/commands/bukkit/core/BukkitActor, have different Class objects for the type revxrsal/commands/CommandHandler used in the signature (revxrsal.commands.bukkit.core.BukkitCommandExecutor is in unnamed module of loader 'spawnbossentities-1.0.jar' @497a00ff, parent loader java.net.URLClassLoader @484b61fc; revxrsal.commands.bukkit.core.BukkitActor is in unnamed module of loader 'customachievementgui-1.0.jar' @7f7069cd, parent loader java.net.URLClassLoader @484b61fc)
	at revxrsal.commands.bukkit.core.BukkitCommandExecutor.onTabComplete(BukkitCommandExecutor.java:44) ~[spawnbossentities-1.0.jar:?]
	at org.bukkit.command.PluginCommand.tabComplete(PluginCommand.java:138) ~[purpur-api-1.19.3-R0.1-SNAPSHOT.jar:?]
	... 22 more

This is how I am initializing the autocompleter for this case (In the main class in #onEnable()):

        BukkitCommandHandler commandHandler = BukkitCommandHandler.create(this);
        ConfigurationSection entities = getConfig().getConfigurationSection("entities");
        if (entities != null)
            commandHandler.getAutoCompleter().registerSuggestion("entityName", SuggestionProvider.of(entities.getKeys(false)));
        else log.severe("Could not find configuration-section \"entities\". Please check your configuration");

        entityHandler = new EntityHandler(this);

        commandHandler.register(new ReloadCommand(this));
        commandHandler.register(entityHandler);
        commandHandler.registerBrigadier();

And this is the starting part of my command's method:

@Command("spawnBossEntity")
@CommandPermission("spawnBossEntities.spawnBossEntity")
@AutoComplete("@entityName ~ ~ ~")
private void spawnEntityCommand(Player sender, @Named("entityName") String entityName,
                                    @Named("locationX") @Optional(def = "~") String locationX,
                                    @Named("locationY") @Optional(def = "~") String locationY,
                                    @Named("locationZ") @Optional(def = "~") String locationZ) {

Could you please explain me what exactly is going wrong here and how I would be able to fix it?
Thanks in advance :)

[Enhancement] Kotlin Extensions for AutoCompleter

There are currently no extensions or additional functions for the AutoCompleter system. This currently results in having to write some pretty ugly looking code.

What the code looks like in Kotlin currently:

commandHandler.autoCompleter.registerParameterSuggestions(String::class.java) { args, sender, command ->
	emptyList<String>()
}

What it could look like in Kotlin (This example follows the current naming scheme of the extensions):

commandHandler.autoCompleter.parameterSuggestions<String> { args, sender, command ->
	emptyList<String>()
}

Not working "switch" arguments only for players

With those two commands:

	@Subcommand("test")
	public void test(int dumbArg, @Optional Player optionalArg, @Switch boolean switchArg) {}

	@Subcommand("test2")
	public void test2(int dumbArg, @Switch boolean switchArg) {}

It's working normally when I do:

/mycommand test2 4
/mycommand test 5
/mycommand test 5 -switchArgs

But I am getting this:
image
when executing:

/mycommand test2 4 -switchArg
/mycommand test 5 SkytAsul -switchArg

But if I execute all of these commands in console, I get no issue. It's only for players.
I also get normal syntax coloring in chat when typing the 2 failing commands, it only fails after I hit enter.

I can't find a reason why those two commands are failing.

@Optional or @Default not working correctly together @Flag

When we are defining @optional and @Flag at the same time, value of parameter will be <?null>
Example command:

@Command("test")
public class TestCommand {
    @DefaultFor("test")
    public void onDefault(String first, @Flag("second") @Optional String second) {
        System.out.println(first);
        System.out.println(second);
    }
}

When we execute /test 1 It`ll print out:

1
<?null>

(Second value is plain String)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.