Skip to content

v2.0.0

Choose a tag to compare

@Mqzn Mqzn released this 21 Aug 05:09
· 331 commits to master since this release

Mevera Imperat (Good bye Velix, Welcome Mevera)

The hottest update of all-time, it includes alot of breaking changes.
Refactored the whole package(and dependency) to the group studio.mevera

Breaking Changes:

  • Removed ResolvedContext its functionality has been fully replaced by ExecutionContext.

  • Removed The old help API (replaced by a new one) & Removed the option helpProvider.

  • Removed the annotation @Help.

  • Removed the option injectCustomHelp from bukkit-platform.

  • Refactored all Imperat#dispatch methods to Imperat#execute and they all return ExecutionResult which is a NEW object introduced , whose purpose is to summarize the results of an attempt of an execution.

  • Refactored S.U.R algorithm to become PVA , which stands for ParameterValueAssigner, this new system is more scalable as the original algorithm has been divided using a design pattern to be more. readable, maintainable and easy to contribute to!

  • Refactored all of our exceptions to require Context as a parameter in their constructor.

  • Recoded our context-matching algorithm in the tree to be more solid, more powerful and 2X faster than the old implementation.

  • Recoded our auto-completion algorithm in the tree to be 10X faster in fetching suggestions.

  • Renamed PermissionResolver to PermissionChecker.

  • Fixed the option overlapOptionalParameterSuggestions not working properly.

  • Fixed the @GlobalAttachmentMode annotation not working properly.

  • Fixed ExecutionContext not being context-resolved properly despite having @ContextResolved on its class.

  • Fixed true-flags input values not being suggested automatically when auto-completing.

  • Fixed @Permission not working properly when placed on root-command-classes.

  • Fixed NPE when loading Imperat during runtime e.g: when using paper library loader.

  • Added More data within PermissionDeniedException , e.g: lackingPermission, usage, inaccessibleParameter

  • Added Lazy fetching of the closest-usage when a InvalidSyntaxException is thrown (for better performance).

  • Added NEW exception layer ProcessorException that wraps around all exceptions thrown during the runtime of a Processor whether its PRE or POST processor.

  • Added NEW option globalDefaultUsageBuilder which determines a default-builder for the default-usage of any command or sub-command being constructed.

  • Added NEW option defaultAttachmentMode which sets a global AttachmentMode for every subcommand being registered.

  • Added NEW option helpCoordinator which lets you customize the Help API.

  • Integrated true-flags input values suggestion resolving with brigadier's integration.

NEW Help API (EXPERIMENTAL)

Introducing NEW Modern Help-API, which as 3 core components:

  • HelpQuery
  • HelpEntry
  • HelpRenderer

The user enters a HelpQuery to query, returning a list of HelpEntry (the HelpProvider is an interface responsible for returning or providing the HelpEntryList) , which is then processed by the HelpDataTransformer to be transformed into a specific object to best represent the structure of the layout, then they are displayed using HelpRenderer in a proper format using options from HelpRenderOptions, which consists of a HelpTheme and a HelpRenderOptions.Layout.

There are two premade layouts to use, one that shows the usages in a listed layout,
while the other organizes them into a tree-like branches.

The core components works together using CommandHelp<YourSource> object (which has a built-in context-resolver for it).
Here's an example of using the new help API with annotations:

@Command("example")
public final class ExampleCommand {

    @Usage
    public void defaultUsage(YourSource source, @ContextResolved CommandHelp<YourSource> commandHelp) {
        //Fetches help when user executes '/example'
        commandHelp.display(
                HelpQuery.<YourSource>builder()
                        .filter(HelpFilters.hasPermission(source, commandHelp)) //filters entries that the user has access to.
                        .build(),
                
                HelpRenderOptions.<YourSource>builder()
                        .layout(HelpRenderOptions.Layout.LIST)
                        .theme(HelpTheme.defaultTheme())
        );
    }
}

The CommandHelp orginally uses HelpCoordinator to coordinate all the core-components to display the help.
based on the query, layout, the transformer and the renderer used.
You can define/customize the transformer or the renderer itself of a layout to your own by using the config option #helpCoordinator and creating your own HelpCoordinator instance.
Quick Bukkit example:

BukkitImperat.builder(plugin)
    .helpCoordinator(
        HelpCoordinator.create((layoutManager)-> {
            layoutManager.registerPipeline(layout, transformer, renderer);
        })
    )
    .build();

Annotated Exception Handling (NEW)

We added @ExceptionHandler(YourException.class) annotation to be placed on methods of a whole separate class, specifically made
for the handling of all throwables/exceptions/errors globally.
Small example

public final class GlobalErrorHandler {
    
    @ExceptionHandler(PermissionDeniedException.class)
    public void handleNoPermission(PermissionDeniedException ex, Context<YourSource> ctx) {
        var src = ctx.source();
        String lackingPermission = ex.getLackingPermission();
        src.reply("You lack the permission '" + lackingPermission + "' to do this!");
    }
    
    //you can add other methods to handle multiple different exceptions here.
}

Then it must be registered AFTER the creation of your imperat's instance.
Example:

imperat = ...;
imperat.registerThrowableHandler(new GlobalErrorHandler());

This solution offers a more compressing way to register all of your error handlers in one place, with better readability , instead of
chaining a myriad of #throwableResolver option from the config.

Handling exceptions per command

You can also define exception handlers that are only SPECIFIC to the command being executed NOT globally.
Example:

@Command("example")
public class ExampleCommand {
     
     @Usage
     public void exec(YourSource source, @Range(min= 5, max=10) Integer num) {
        source.reply("Entered number: " + num);
     }
    
     @ExceptionHandler(NumberOutOfRangeException.class)
     public void handleNumberOutOfRange(NumberOutOfRangeException ex, Context<YourSource> ctx) {
        NumericRange range = ex.getRange();
        final StringBuilder builder = new StringBuilder();
        if (range.getMin() != Double.MIN_VALUE && range.getMax() != Double.MAX_VALUE)
            builder.append("within ").append(range.getMin()).append('-').append(range.getMax());
        else if (range.getMin() != Double.MIN_VALUE)
            builder.append("at least '").append(range.getMin()).append("'");
        else if (range.getMax() != Double.MAX_VALUE)
            builder.append("at most '").append(range.getMax()).append("'");
        else
            builder.append("(Open range)");
        
        String rangeFormatted = builder.toString();
        ctx.source().reply(
                "Value '" + ex.getValue() + "' entered for parameter '"
                        + ex.getParameter().format() + "' must be " + rangeFormatted
        );
     }
     
}

So when ONLY when executing the command above, when a NumberOutOfRangeException is thrown, it will be handled through the
handler that is attached to that specific command for that type of exception.
Other commands may have their own handlers for the same type.

TIP: Specific commands exception handlers take precedence over the global exception handlers.

Auto Permission Assigner (NEW)

This is a new hot feature.
When this feature is enabled, it will automatically every possible pathway/usage of a command a permission per parameter.
You can enable it through the NEW option autoPermissionAssignMode.
Therefore, on complex command structure with many possible pathways and subcommands (many usages) you will not need to assign a permission to each and every subcommand or to each and every single parameter, this will be done automatically.

This feature has 2 components:

  • PermissionLoader which determines and resolves permissions for command parameters, you can add your own loader through the NEW option: #permissionLoader in the config.
  • NodePermissionAssigner which determines how defines the strategy for how permissions
    loaded by the PermissionLoader are actually applied to individual
    ParameterNode instances (core component of Imperat's CommandTree).
    It can be customized by adding your own node assigner through the NEW option: #permissionAssigner in the config.