Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Getting Started

xRa1ny edited this page May 20, 2023 · 2 revisions

Getting Started

Maven

This API is currently NOT stored within any open maven repository, which means you'll have to clone the GitHub project and install it in your local maven repo manually...

NMS

This API is built with the 1.16.5 SpigotAPI, featuring any NMS related content, which means, you'll have to setup NMS if you plan on using it in YOUR build of the project (You can also change the SpigotAPI dependency inside the pom.xml after you clone the project if you don't intend on using NMS)

If you do want to use NMS but don't have it installed yet, you can download the last successful build of BuildTools here
After you have downloaded BuildTools, you can run the following to install a NMS ready SpigotAPI build to your local maven repo:

java -jar BuildTools.jar --rev 1.19.3 # <- CHANGE VERSION NUMBER TO YOUR VERSION

User Management

This API has a built in user manager which stores a list of all registered users as a class instance.
You can specify your own user by creating a class that extends the default User class: RUser
These users will be used to create custom content whenever needed and also store the player
All API Methods will use the User as a parameter whenever a player is needed, to ensure you can always have access to a user instance whenever an action is performed on a player.
You can always access the user manager by calling its getter inside the main class: RPlugin.getInstance().getUserManager()

First Steps

After successfully installing the project to your local m2, we can actually start working with it!

The first things you need to know, are the 'conventions' used within the project

Every class which has the prefix 'R' is designed to be extended or implement from

public class MyAwesomePlugin extends RPlugin {
    ...
}
public class MyAwesomeCommand extends RCommand {
    ...
}
public class MyAwesomeItemStack extends RItemStack {
    ...
}
public class MyAwesomeUser extends RUser {
    ...
}

The API also uses annotation to simplify and reduce code whenever possible
So you'll find many occasions, where an info annotation is being used to parse meta information to a class instance

These 'conventions' are used everywhere in this project and wont change in the future

Your first Plugin

This API does not change the way you would go about creating a normal plugin, it just 'enhances' the way you would do it normally

To start off, you need to create a main class for your plugin, in our example, lets call it 'MyPlugin'
Every plugin you develop with this API, needs to have a main class, which extends from the RPlugin class like shown below

@PluginInfo(userClass = HubUser.class) // <- NOT NEEDED - If you dont supply this class with a PluginInfo annotation, the default RUser.class will be used
public class MyPlugin extends RPlugin {
    @Override
    public void onPluginEnable() {
        ...
    }

    @Override
    public void onPluginDisable() {
        ...
    }
}

As you can see here, we use the PluginInfo annotation to parse in meta information to our plugin...
Here we specify the class that should be used for User Management (If you want to use your own User class, for example HubUser, BedwarsUser, SkywarsUser, etc)

You currently also still have to create your 'plugin.yml' but this is planned to be changed in the future...

name: MyPlugin
version: 1.0
main: PACKAGE.TO.YOUR.MAIN.CLASS.MyPlugin
api-version: 1.16

Thats it!
Now you only have to maven package and deploy your build to a 1.16.5 server and your done!

Your first Listener

Setting up this API and creating your first main class, was only the tip of the iceberg
Now we'll get into creating your first listener...
The way you would normally create a listener has not changed, the way you would register it though has changed a bit
Lets get started!
To create a listener, create a class that implements from the Listener interface like this:

public class MyListener implements Listener {
    ...
}

This class now acts as an event listener for all sorts of events that might take place on our server
But currently, there is not code inside our class, which handles said events
For example, to handle an event which fires when a block breaks, you would implement it this way:

public class MyListener implements Listener {
    @EventHandler
    public void onPlayerBlockBreak(@NotNull BlockBreakEvent e) { // <- THE NAME OF THIS METHOD CAN BE WHATEVER YOU WANT
        e.getPlayer().sendMessage("You broke a block of type: " + e.getBlock().getType());
    }

    ...
}

Now that we have a listener that handles a BlockBreakEvent, we can go ahead and register it when our plugin enables...
To do this, we head on over back to our main class and register the listener we just created within our onPluginEnable() Method:

public class MyPlugin extends RPlugin {
    @Override
    public void onPluginEnable() {
        // LISTENER
        getListenerManager().register(new MyListener());                        // <- Singular listener registration
        getListenerManager().registerAll("PACKAGE NAME OF MULTIPLE LISTENERS"); // <- Multiple listener registration
        Bukkit.getPluginManager().registerEvents(this, new MyAwesomeListener()) // <- WORKS, BUT NOT RECOMMENDED!
    }

    @Override
    public void onPluginDisable() {
        ...
    }
}

DONE!
Thats all we need to create and register a fully functioning listener!

Your first Interactable Item

With this API, you are able to easily create an item, which has a left and right click function, and also a cooldown!
First off, we need to create a class that extends from the RItemStack class, lets call it 'MyItem'
This class represents the Item we wish to create

@ItemStackInfo(name = "", lore = {}, amount = 0, type = null, itemFlags = {}, cooldown = 0) // Here we specify the meta information of our item
public class MyItem extends RItemStack {
    @Override
    public boolean onLeftClick(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...

        return false;
    }

    @Override
    public boolean onRightClick(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...

        return false;
    }

    @Override
    public void onCooldown(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onCooldownExpire(@NotNull RUser user) {
        ...
    }
}

As you can see, with this implementation, we are using an annotation called ItemStackInfo, you will see this pattern all over the API
With this annotation, we can specify our item's 'data' (name, lore, type, amount, etc)
This annotation replaces the need to manually set every property using methods
The API handles all of the background work of using every property that is specified within the ItemStackInfo annotation so you dont have to worry about that!

If you dont want to use this annotation, you can also call the super() constructor within the class that extends the RItemStack class like this:

public class MyItem extends RItemStack {
    public MyItem() {
        super(ITEMSTACK INSTANCE);
    }

    @Override
    public boolean onLeftClick(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...

        return false;
    }

    @Override
    public boolean onRightClick(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...

        return false;
    }

    @Override
    public void onCooldown(@NotNull PlayerInteractEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onCooldownExpire(@NotNull RUser user) {
        ...
    }
}

It is good practice, to create only one instance of this item (Singleton)
I normally create an interface where I store every item of this plugin (MyItems)

After creating our item, we need to register it, the way we registered our listener
So lets head on over back to our main class and register our newly created item!

public class MyPlugin extends RPlugin {
    @Override
    public void onPluginEnable() {
        // LISTENER
        getListenerManager().register(new MyListener());                        // <- Singular listener registration
        getListenerManager().registerAll("PACKAGE NAME OF MULTIPLE LISTENERS"); // <- Multiple listener registration
        Bukkit.getPluginManager().registerEvents(this, new MyAwesomeListener()) // <- WORKS, BUT NOT RECOMMENDED!


        // ITEMSTACK
        getItemStackManager().register(new MyItem());                            // <- Singular itemstack registration
        getItemStackManager().registerAll("PACKAGE NAME OF MULTIPLE LISTENERS"); // <- Multiple itemstack registration
    }

    @Override
    public void onPluginDisable() {
        ...
    }
}

DONE!
Thats all we need to create and register a fully functioning item!

Your first Command

Creating Commands has always been a struggle with the normal SpigotAPI since you always have to check the length and validity of the parsed command arguments on execution
To create your own Command, you need to create a new class which extends from the RCommand class, lets call it 'MyCommand'

@CommandInfo(name = "mycommand", permission = "permission.command.mycommand", requiresPlayer = false, args = {"arg1", "arg1 %PLAYER%", "arg1 %PLAYER% arg2"})
public class MyCommand extends RCommand {
    @Override
    protected @NotNull CommandReturnState executeBaseCommand(@NotNull CommandSender sender) { // <- Called when only the base command has been executed (/mycommand)
        return RCommandReturnState.SUCCESS; // <- There are currently 3 valid return states, that represent an execution of the command (SUCCESS, INVALID_ARGS, ERROR)
    }

    @Override
    protected @NotNull CommandReturnState executeWithArgs(@NotNull CommandSender sender, @NotNull String args, @NotNull String[] values) {
        return RCommandReturnState.SUCCESS;
    }

    @Override
    protected @NotNull @Unmodifiable List<String> help(@NotNull CommandSender sender) {
        return List.of();
    }
}

As you can see here, we also use an annotation to parse our meta information to our command
Whats interesting about this though, is that we also have an option to specify the arguments of our command...
This field is responsible for specifying all valid arguments on this command in form of a string array

To properly use this, you have to understand how the arguments are being parsed to you...
Whenever you execute a command with arguments, the executeWithArgs() Method will be called...
Lets assume we run the command like so: /mycommand arg1 xRa1ny
Since we defined in our CommandInfo that the arguments arg1 %PLAYER% are valid, we can expect the following inside the executeWithArgs() Method:
Our args parameter will look like this: "arg1 ?" and our values string array like this: xRa1ny

Running a command with an argument that has not been specified in the CommandInfo, it will always be replaced with a ? inside the args parameter
The arguments only know what YOU define, not what a user types in the chat when executing a command
When you run a command with an 'unknown' argument, it will always be put inside the values string array

For instance, lets say we run this command like so: /mycommand test1 test2
What would be the value of args and values?
args: ? ?
values: "test1", "test2"

Now that you know how the command interprets arguments, you only need to know what the help() Method is used for...
This Method defines what to display when running a command with the help argument
For instance /mycommand help
This argument is hard coded into every command and will always be available

And last but not least, TabCompletion!
We all want it but are sick of coding it up...
With the API you dont have to worry about anything TabCompletion related, it gets handled automatically!
Every argument you define in the CommandInfo, gets handled automatically in TabCompletion, removing the step of coding it up yourself!

But now you might ask yourself, what if I want some other user input in my arguments, not only %PLAYER%
Good Question!
The API handles custom arguments very well, you can always make up your own pattern and use it within your arguments
For instance, there is no pattern coded up for %USER% or %TEST% or %123% but we can still use them in our code!
If we use %TEST% our TabComplete will look like this: <TEST>

Oookay, now that we have command creation out of the way, we want to register this command...
So again, we head on over back to our main class and register the command within our onPluginEnable():

public class MyPlugin extends RPlugin {
    @Override
    public void onPluginEnable() {
        // LISTENER
        getListenerManager().register(new MyListener());                          // <- Singular listener registration
        getListenerManager().registerAll("PACKAGE NAME OF MULTIPLE LISTENERS");   // <- Multiple listener registration
        Bukkit.getPluginManager().registerEvents(this, new MyAwesomeListener())   // <- WORKS, BUT NOT RECOMMENDED!


        // ITEMSTACK
        getItemStackManager().register(new MyItem());                             // <- Singular itemstack registration
        getItemStackManager().registerAll("PACKAGE NAME OF MULTIPLE ITEMSTACKS"); // <- Multiple itemstack registration

        // COMMAND
        getCommandManager().register(new MyCommand());                            // <- Singular command registration
        getCommandManager().registerAll("PACKAGE NAME OF MULTIPLE COMMANDS");     // <- Multiple command registration
    }

    @Override
    public void onPluginDisable() {
        ...
    }
}

Now, after registering the command inside our main class, we also need to add it to our plugin.yml

name: MyPlugin
version: 1.0
main: PACKAGE.TO.YOUR.MAIN.CLASS.MyPlugin
api-version: 1.16
commands:
  mycommand:

DONE!
Thats all we need to create and register a fully functioning command!

Your first Inventory Menu

Want to create an interactive inventory?
Well with this API you are able to do that in no time!
To start off, create a new class that extends RInventoryMenu, lets call it 'MyMenu'

@InventoryMenuInfo(title = "", size = 0, background = null)
public class MyMenu extends RInventoryMenu {
    public MyMenu(@Nullable RInventoryMenu previousMenu) { // <- You can remove this from params if you dont need to parse it anyways...
        super(previousMenu); // <- Here we specify the last menu we had open, can be null if you wont use this
    }

    @Override
    public void setItems(@NotNull RUser user) {
        ...
    }

    @Override
    public void onOpen(@NotNull InventoryOpenEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onClose(@NotNull InventoryCloseEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onClick(@NotNull InventoryClickEvent e, @NotNull RUser user) {
        ...
    }
}

As you can see, here we are also using an annotation where we pass meta information to our menu
The size field, must always be a multiple of 9 because of Minecraft limitations
The background field, specifies the material that fills every slot of our inventory as background (item with no name gets created)

The setItems() Method gets called when a user opens this inventory, here we set every item we want in our menu
The onOpen() Method, as it suggests, gets called when this menu opens (before setItems())
The onClose() Method, as it suggests, gets called when this menu closes
The onClick() Method, as its suggests, gets called whenever a player click within the inventory of this menu

Good practice, is to always create a new instance for every opening sequence of this menu
For instance: we want to open a menu when a player right clicks an item, we create a new instance of this menu an call open() on it....

new MyMenu(null).open(user); // <- As the user instance, we can use RPlugin.getUserManager().getUser(player); if we dont have direct access to a user

DONE! Thats all we need to create a fully functioning InventoryMenu!

Your first PagedInventoryMenu

We approach this the same way we do when creating a normal InventoryMenu, but now extend RPagedInventoryMenu instead...

@InventoryMenuInfo(title = "", size = 0, background = null)
public class MyMenu extends RPagedInventoryMenu {
    public MyMenu(@Nullable RInventoryMenu previousMenu) {
        super(previousMenu);
    }

    @Override
    protected void onPageChange(int page) {
        ...
    }

    @Override
    public void setItems(@NotNull RUser user) {
        ...
    }

    @Override
    public void onOpen(@NotNull InventoryOpenEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onClose(@NotNull InventoryCloseEvent e, @NotNull RUser user) {
        ...
    }

    @Override
    public void onClick(@NotNull InventoryClickEvent e, @NotNull RUser user) {
        ...
    }
}

The only difference to a normal InventoryMenu is that we now have a method called onPageChange()
This Method will be called every time we call setPage()

DONE! Thats all we need to create a fully functioning PagedInventoryMenu!

Clone this wiki locally