Foundation Minecraft Plugin Framework

repository·v6·Indexed 19 days ago

https://github.com/kangarko/foundation

A high-performance Minecraft plugin framework for Spigot, Paper, and Folia (versions 1.8.8+) designed to reduce boilerplate. It provides cross-version compatibility and simplifies GUI management, command handling via SimpleCommand, database integration, and packet manipulation. Key features include a streamlined plugin lifecycle via SimplePlugin, robust command argument parsing, built-in tab completion utilities, and a permission system using @Permission and @PermissionGroup annotations.

Tokens
7K
Snippets
15
Records
26
Agent score
65%

What's inside Foundation

  1. Foundation Compatibility and Licensing

    v6

    Compatibility

    Foundation provides a compatibility layer for Minecraft versions 1.8.8 through the latest version. It supports Spigot, Paper, Folia, and most forks.

    Licensing

    • Paying Students of MineAcademy.org: Can use, modify, and reproduce Foundation commercially and non-commercially without attribution.
    • Non-paying users: May use the library but must clearly attribute Foundation by linking to its GitHub page (e.g., on your Spigot overview page).
    • Restrictions: Do not sell or claim any part of the library as your own.
  2. Quick Start: Migrating from JavaPlugin to SimplePlugin

    v6

    To use Foundation, you must migrate your main plugin class from the standard Spigot/Paper JavaPlugin to Foundation's SimplePlugin. This allows Foundation to automatically handle registration and listeners.

    Required Changes:

    1. Change extends JavaPlugin to extends SimplePlugin.
    2. Rename onEnable() to onPluginStart().
    3. Rename onDisable() to onPluginStop().
    4. If you use a static getInstance() method, update it to return (T) SimplePlugin.getInstance() (where T is your plugin class). Remove any local instance variables like myPlugin = this from your class.
    public class MyPlugin extends SimplePlugin {
    
        @Override
        public void onPluginStart() {
            // Logic previously in onEnable()
        }
    
        @Override
        public void onPluginStop() {
            // Logic previously in onDisable()
        }
    
        public static MyPlugin getInstance() {
            return (MyPlugin) SimplePlugin.getInstance();
        }
    }
  3. Import Foundation via Maven

    v6

    Foundation is hosted on JitPack. To import it into your Maven project, add the JitPack repository and the Foundation dependency to your pom.xml.

    <!-- In <repositories> section -->
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
    
    <!-- In <dependencies> section -->
    <dependency>
        <groupId>com.github.kangarko</groupId>
        <artifactId>Foundation</artifactId>
        <version>REPLACE_WITH_LATEST_VERSION</version>
    </dependency>
  4. Configure Maven Shading for Foundation

    v6

    Because Foundation includes optional dependencies (like WorldEdit) that you may not want in your final JAR, you must configure the maven-shade-plugin to only include Foundation and any other specific libraries you explicitly need.

    Crucial Steps:

    1. Use the <includes> section to limit shaded artifacts to com.github.kangarko:Foundation*.
    2. Use the <relocations> section to move org.mineacademy.fo into your own package (e.g., your.plugin.package.lib) to prevent version conflicts with other plugins.
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.5.1</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <createDependencyReducedPom>false</createDependencyReducedPom>
            <artifactSet>
                <includes>
                    <!-- Only Foundation and your specific required libraries go here -->
                    <include>com.github.kangarko:Foundation*</include>
                </includes>
            </artifactSet>
            <relocations>
                <relocation>
                    <pattern>org.mineacademy.fo</pattern>
                    <shadedPattern>your.plugin.main.package.lib</shadedPattern>
                </relocation>
            </relocations>
        </configuration>
    </plugin>
  5. Use the Placeholder System in Command Messages

    v6

    Messages sent via returnTell or defined in command configuration support several types of placeholders:

    PlaceholderDescription
    {prefix}The command's tell prefix (via Common.getTellPrefix())
    {label}The command's primary label
    {current_label}The label currently being used (useful for aliases)
    {sublabel}The first sub-command or argument (depending on command type)
    {current_sublabel}The active sub-label
    {0}, {1}, etc.The command arguments (e.g., {0} is the first argument)

    Additionally, the system integrates with Variables.replace(message, null) to support custom variable replacements.

  6. How SimpleCommandGroup handles subcommands and help

    v6

    A SimpleCommandGroup acts as a container for SimpleSubCommand instances. It manages the routing of the main command to the appropriate subcommand based on the first argument.

    Automatic Help System:

    • The group automatically handles help triggers. By default, these are help and ? (e.g., /label help).
    • When a help command is triggered, the group generates a paginated, colorized menu showing all subcommands the sender has permission to use.
    • The help menu includes hoverable tooltips for descriptions and permissions (on Minecraft 1.17+).
    • You can customize the help behavior by overriding sendHelpIfNoArgs() (to show help instead of the default header when no arguments are provided) and getHelpLabel() (to change the trigger words).

    Tab Completion:

    • The group provides automatic tab completion for subcommand labels.
    • If a subcommand is identified, the group delegates further tab completion to that specific SimpleSubCommand instance.
  7. Register subcommands automatically

    v6

    For SimpleSubCommand to function correctly, your plugin must define a SimpleCommandGroup.

    To ensure automatic registration, your class extending SimpleCommandGroup must:

    1. Be annotated with @AutoRegister.
    2. Have a no-args constructor.

    If the plugin cannot find a main command group via SimplePlugin.getInstance().getMainCommand(), it will throw an error: [PluginName] does not define a main command group! You need to put @AutoRegister over your class extending a SimpleCommandGroup that has a no args constructor to register it automatically.

  8. Implement a command group with SimpleCommandGroup

    v6

    To create a hierarchical command structure (e.g., /arena join, /arena leave), extend SimpleCommandGroup. You must implement the registerSubcommands() method to define the subcommands belonging to the group.

    Key lifecycle steps:

    1. Instantiate: Use a constructor to define the main label and aliases. You can use a single string with | or / as a delimiter to define both (e.g., "channel|ch" creates /channel with alias /ch).
    2. Register Subcommands: Inside registerSubcommands(), use registerSubcommand(SimpleSubCommand) to add individual commands or registerSubcommand(Class<? extends SimpleSubCommand>) to automatically register all final child classes of a specific type.
    3. Register Group: Call register() to bind the group to Bukkit.
    4. Unregister: Call unregister() to remove the command from the server.
    public class MyCommandGroup extends SimpleCommandGroup {
        public MyCommandGroup() {
            super("mycmd|mc"); // Label: /mycmd, Alias: /mc
        }
    
        @Override
        protected void registerSubcommands() {
            // Register individual subcommands
            registerSubcommand(new MyJoinSubCommand());
            
            // Or auto-register all final subclasses of a specific type
            registerSubcommand(MyBaseSubCommand.class);
            
            // Add a help line/filler
            registerHelpLine("&7Use these commands to manage your profile");
        }
    }
  9. Implement a custom command with SimpleCommand

    v6

    To create a new command, extend the SimpleCommand abstract class. You must implement the onCommand() method, which contains the actual logic for your command. The execute() method is handled by the base class, which automatically manages permissions, minimum argument checks, cooldowns, and error handling.

    Key Lifecycle & Features

    • Registration: Use .register() to add the command to Bukkit. You can use .register(true, unregisterOldAliases) to handle potential conflicts with existing commands.
    • Arguments: Access command arguments via the args array and the command sender via the sender object (both updated dynamically during execution).
    • Help System: If autoHandleHelp is true (default), running the command with help or ? will automatically display your usage message. You can customize this by overriding getMultilineUsageMessage().
    • Cooldowns: You can set a cooldown in seconds using setCooldownSeconds(int). Players can bypass this if they have the permission specified by setCooldownBypassPermission(String).
    public class MyCommand extends SimpleCommand {
    
        public MyCommand() {
            // Use '|' to separate the main label from aliases
            super("test|t|testcmd");
            setCooldownSeconds(10);
            setMinArguments(1);
        }
    
        @Override
        protected void onCommand() {
            // Your logic here
            tellSuccess("Command executed successfully!");
        }
    
        @Override
        protected String[] getMultilineUsageMessage() {
            return new String[] {
                "Usage: /test <name>",
                "<name> - The name of the player to target"
            };
        }
    }
  10. Use FastMatcher for high-performance pattern matching

    v6

    FastMatcher is a high-performance utility designed for efficient string matching, specifically optimized for scenarios where standard regex is too slow (e.g., evaluating many items against many rules). It supports four distinct matching modes based on the pattern syntax used:

    1. Starts With: Prefix the pattern with * (e.g., *DIAMOND_ matches DIAMOND_SWORD).
    2. Ends With: Suffix the pattern with * (e.g., _SWORD* matches DIAMOND_SWORD).
    3. Exact Match: Wrap the pattern in double quotes (e.g., "DIAMOND_SWORD" matches only DIAMOND_SWORD).
    4. Contains: The default mode if no special characters are used (e.g., DIAMOND matches DIAMOND_SWORD).

    Advanced Features:

    • Wildcard: Use * to match everything.
    • OR Logic: Use the pipe | character to separate multiple patterns (e.g., DIAMOND_*|GOLDEN_*).
    • Regex Fallback: To use standard Java Regular Expressions, prefix your pattern with * (a star followed by a space). For example, * ^DIAMOND_(SWORD|HOE) will trigger regex evaluation.
    // Compile a matcher from a pattern string
    FastMatcher matcher = FastMatcher.compile("DIAMOND_*|GOLDEN_*");
    
    // Check if a message matches
    boolean isMatch = matcher.find("DIAMOND_SWORD"); // returns true
    boolean isNotMatch = matcher.find("SUPERDIAMOND_SWORD"); // returns false
    
    // Using Regex fallback
    FastMatcher regexMatcher = FastMatcher.compile("* ^DIAMOND_(SWORD|HOE)");
    boolean regexMatch = regexMatcher.find("DIAMOND_SWORD"); // returns true
  11. Manipulate Command Arguments

    v6

    The SimpleCommand class provides several utility methods to work with the args array:

    • getLastArg(): Returns the last argument provided, or an empty string if no arguments exist.
    • rangeArgs(int from): Returns a copy of arguments from the specified index to the end.
    • rangeArgs(int from, int to): Returns a copy of arguments within the specified range.
    • joinArgs(int from): Returns the arguments from the specified index to the end, joined by spaces as a single String.
    • joinArgs(int from, int to): Returns the arguments within the specified range, joined by spaces.
    • setArg(int position, String value): Safely updates or expands the arguments array at a specific position.
  12. Implement Tab Completion in SimpleCommand

    v6

    To provide tab completion suggestions for your custom command, override the tabComplete() method.

    Key behaviors:

    • The framework automatically checks for the required permission (getPermission()) before calling your method.
    • If you return null, the framework will automatically attempt to complete the last word as a player name (simulating standard Bukkit behavior).
    • You can access the current command state using the sender, label, and args fields provided by the SimpleCommand class.

    For convenience, use the following utility methods to filter suggestions based on the last argument typed:

    • completeLastWord(T... suggestions): Sorts and filters a list of suggestions.
    • completeLastWord(Iterable<T> suggestions): Works with any Iterable.
    • completeLastWord(Iterable<T> suggestions, Function<T, String> toString): Works with Iterables by applying a mapping function.
    • completeLastWordWorldNames(): Suggests available world names.
    • completeLastWordPlayerNames(): Suggests online player names (respects visibility/vanish settings).
    @Override
    protected List<String> tabComplete() {
        // Use completeLastWord to filter suggestions based on what the user typed
        return completeLastWord("option1", "option2", "option3");
    }