Skip to main content
This guide will walk you through creating a simple plugin using Foundation, from setup to your first working command.

Before you begin

Make sure you have:
  • Completed the installation guide
  • A Java IDE (IntelliJ IDEA, Eclipse, or VS Code)
  • Basic understanding of Java and Minecraft plugins
Important: Do not skip any steps in this guide. Missing the shading configuration or SimplePlugin setup will cause your plugin to break.

Creating your plugin

1

Convert to SimplePlugin

Change your main plugin class from extending JavaPlugin to extending SimplePlugin:
import org.mineacademy.fo.plugin.SimplePlugin;

public class MyPlugin extends SimplePlugin {
    
    // Your code here
}
This gives you access to Foundation’s automatic listener registration, configuration management, and enhanced APIs.
2

Replace lifecycle methods

Foundation uses different method names for startup and shutdown:Before (JavaPlugin):
@Override
public void onEnable() {
    // Startup logic
}

@Override
public void onDisable() {
    // Shutdown logic
}
After (SimplePlugin):
@Override
protected void onPluginStart() {
    // Startup logic
    Common.log("MyPlugin has started!");
}

@Override
protected void onPluginStop() {
    // Shutdown logic
    Common.log("MyPlugin is stopping...");
}
Foundation occupies onEnable() and onDisable() to perform automatic registration and cleanup, so you must use onPluginStart() and onPluginStop() instead.
3

Update getInstance() method

If you use a static getInstance() method, update it to use Foundation’s instance management:Before:
private static MyPlugin instance;

@Override
public void onEnable() {
    instance = this;
}

public static MyPlugin getInstance() {
    return instance;
}
After:
// Remove the instance field and initialization
// private static MyPlugin instance; ❌ DELETE THIS
// instance = this; ❌ DELETE THIS

public static MyPlugin getInstance() {
    return (MyPlugin) SimplePlugin.getInstance();
}
Foundation automatically manages the plugin instance, so you don’t need to store it yourself.
4

Verify shading configuration

Before proceeding, double-check your pom.xml or build.gradle has the proper shading configuration as described in the installation guide.Your maven-shade-plugin should include:
<includes>
    <include>com.github.kangarko:Foundation*</include>
</includes>
And relocate Foundation:
<relocation>
    <pattern>org.mineacademy.fo</pattern>
    <shadedPattern>your.plugin.main.package.lib</shadedPattern>
</relocation>

Creating your first command

Let’s create a simple command without using plugin.yml:
1

Create command class

Create a new class extending SimpleCommand:
import org.mineacademy.fo.command.SimpleCommand;
import org.bukkit.entity.Player;

public class HelloCommand extends SimpleCommand {

    public HelloCommand() {
        super("hello");
        
        setDescription("Say hello to the player");
        setUsage("/<command> [player]");
    }

    @Override
    protected void onCommand() {
        checkConsole(); // Ensure only players can run this
        
        Player player = getPlayer();
        
        if (args.length == 0) {
            tell("Hello, " + player.getName() + "!");
        } else {
            Player target = findPlayer(args[0]);
            tell("You said hello to " + target.getName() + "!");
        }
    }
}
Notice there’s no plugin.yml configuration needed. Foundation automatically registers the command.
2

Register the command

In your main plugin class, register the command in onPluginStart():
@Override
protected void onPluginStart() {
    registerCommand(new HelloCommand());
}
That’s it! Your command is now registered and functional.
3

Add tab completion (optional)

Override the tabComplete() method to add smart tab completion:
@Override
protected List<String> tabComplete() {
    if (args.length == 1)
        return completeLastWord(getOnlinePlayers());
    
    return NO_COMPLETE;
}

Creating a simple menu

Foundation makes GUI menus incredibly easy:
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.mineacademy.fo.menu.Menu;
import org.mineacademy.fo.menu.button.Button;
import org.mineacademy.fo.menu.model.ItemCreator;
import org.mineacademy.fo.remain.CompMaterial;

public class WelcomeMenu extends Menu {

    private final Button infoButton;

    public WelcomeMenu() {
        setTitle("Welcome Menu");
        setSize(9 * 3); // 3 rows
        
        // Create a button
        this.infoButton = new Button() {
            @Override
            public void onClickedInMenu(Player player, Menu menu, ClickType click) {
                player.sendMessage("You clicked the info button!");
            }

            @Override
            public ItemStack getItem() {
                return ItemCreator.of(
                    CompMaterial.BOOK,
                    "Information",
                    "",
                    "Click me for info!"
                ).build().make();
            }
        };
    }

    @Override
    public ItemStack getItemAt(int slot) {
        if (slot == 13) // Center of the menu
            return this.infoButton.getItem();
        
        return null;
    }
}
Open the menu for a player:
new WelcomeMenu().displayTo(player);

Working with configuration

Foundation provides auto-updating configuration with comments:
1

Create settings class

import org.mineacademy.fo.settings.YamlStaticConfig;

public class Settings extends YamlStaticConfig {
    
    public static String WELCOME_MESSAGE;
    public static Integer MAX_PLAYERS;
    public static Boolean DEBUG_MODE;

    @Override
    protected void onLoad() {
        loadConfiguration("settings.yml");
    }

    private static void load() {
        setPathPrefix("Settings");
        
        WELCOME_MESSAGE = getString("Welcome_Message", "Welcome to the server!");
        MAX_PLAYERS = getInteger("Max_Players", 100);
        DEBUG_MODE = getBoolean("Debug_Mode", false);
    }
}
2

Create default config file

Create src/main/resources/settings.yml:
# Main plugin settings
Settings:
  # Message shown to players on join
  Welcome_Message: "Welcome to the server!"
  
  # Maximum number of players
  Max_Players: 100
  
  # Enable debug logging
  Debug_Mode: false
3

Use settings in your code

@Override
protected void onPluginStart() {
    // Settings are automatically loaded
    Common.log("Welcome message: " + Settings.WELCOME_MESSAGE);
    
    if (Settings.DEBUG_MODE) {
        Common.log("Debug mode is enabled");
    }
}

Testing your plugin

1

Build the plugin

mvn clean package
Or for Gradle:
gradle shadowJar
2

Copy to server

Copy the jar from target/ (Maven) or build/libs/ (Gradle) to your test server’s plugins/ folder.
3

Start the server

Start your server and verify the plugin loads:
[Server thread/INFO]: [MyPlugin] Enabling MyPlugin v1.0.0
[Server thread/INFO]: MyPlugin has started!
4

Test your command

Join the server and run:
/hello
You should see: Hello, YourName!

Common utilities

Foundation provides many helpful utilities:

Messaging

import org.mineacademy.fo.Common;

// Send colored messages
Common.tell(player, "&aThis is green text!");

// Broadcast to all players
Common.broadcast("&6Server announcement!");

// Log to console
Common.log("This appears in console");

Scheduling tasks

import org.mineacademy.fo.Common;

// Run task later (in ticks, 20 ticks = 1 second)
Common.runLater(20, () -> {
    player.sendMessage("This runs after 1 second!");
});

// Run task repeatedly
Common.runTimer(20, 20, () -> {
    // Runs every second
    player.sendMessage("Tick!");
});

Player utilities

import org.mineacademy.fo.PlayerUtil;

// Kick player with a message
PlayerUtil.kick(player, "You have been kicked!");

// Get player's ping
int ping = PlayerUtil.getPing(player);

// Check if player has permission
if (PlayerUtil.hasPerm(player, "myplugin.admin")) {
    // Do something
}

Next steps

Plugin Template

Explore a complete example plugin

Project Orion

Complete training course on Foundation

Migration guide

Learn more about converting existing plugins

API Reference

Browse the source code and JavaDocs

Troubleshooting

Plugin fails to load

Check that you:
  1. Extended SimplePlugin instead of JavaPlugin
  2. Used onPluginStart() and onPluginStop() instead of onEnable() and onDisable()
  3. Configured shading properly in your pom.xml or build.gradle

NoClassDefFoundError

You likely didn’t shade Foundation properly. Review the installation guide shading configuration.

Command not working

Ensure you:
  1. Called registerCommand() in onPluginStart()
  2. Extended SimpleCommand properly
  3. Don’t have the command defined in plugin.yml (Foundation handles this automatically)

Build docs developers (and LLMs) love