Revxrsal / Lamp

A modern annotations-driven commands framework for Java and Kotlin
MIT License
207 stars 38 forks source link

Command Resolution Issue: Sibling and Related Commands Not Properly Resolved in Split Command Registrations #114

Open lulu2002 opened 1 week ago

lulu2002 commented 1 week ago

I encountered an issue with the command resolution when using the help command in the current framework, which can resolve similar commands through SiblingCommands, ChildrenCommands, and their combination via RelatedCommands. However, this structure has a flaw when commands are registered separately across different classes.

For example, consider the following scenario:

In Class A:

@Command("base help")

In Class B:

@Command("base reward set")
@Command("base reward get")

In this case, the /base help command does not retrieve the base reward commands, because when checking the siblingPath, the path for base help is "base", while the path for base reward get is "base reward". As a result, neither siblingPath nor relativePath returns the full command help I expect.

Temporary Solution:

To resolve this issue temporarily, I implemented my own custom Help handler, using the following code:

Lamp<BukkitCommandActor> lamp = BukkitLamp.builder(this)
        .parameterTypes((it) -> {
            it.addParameterType(CardGroup.class, injector.getInstance(GroupParameterType.class));
            it.addContextParameterFactory(ContextParameter.Factory.forType(
                    MySiblingCommands.class,
                    (parameter, context) -> new MySiblingCommandsImpl(Collections.filter(context.command().lamp().registry().commands(), (anotherCmd) -> {
                        ExecutableCommand<BukkitCommandActor> currentCommand = context.command();
                        String currentPath = Temp.getSiblingPath(currentCommand).toLowerCase();
                        String anotherPath = Temp.getSiblingPath(anotherCmd).toLowerCase();
                        return anotherCmd != currentCommand && !anotherCmd.isSecret() && anotherPath.startsWith(currentPath) && anotherCmd.permission().isExecutableBy(context.actor());
                    }))
            ));
        })
        .build();

I also used the following helper to retrieve the sibling path:

object Temp {
    @JvmStatic
    fun getSiblingPath(cmd: ExecutableCommand<*>): String {
        val field = Execution::class.java.getDeclaredField("siblingPath")
        field.trySetAccessible()
        return field.get(cmd) as String
    }
}

This solution filters commands based on the siblingPath and uses startsWith to retrieve all related commands.

Request for Enhancement:

I believe it would be highly beneficial for the Lamp framework to natively support such functionality, allowing help commands to resolve all relevant commands, even when registered separately across different classes. This enhancement could improve the flexibility and ease of organizing command registration.

Revxrsal commented 1 week ago

Thank you for bringing this to my attention. Yeah, I'm aware that the sibling implementation is not as comprehensive as it should. I think this goes back to the fact that we may not fully agree on who is a sibling of who :P

Nonetheless, the issue you point out is valid. I'll migrate this implementation when I get a chance. Thanks.

bed-dev commented 1 week ago

We can agree that in this case help is a sibling of reward set, and I already told you this issue before