Skip to content

BukkitOfUtils - Info For Developers

The plugin also comes with a nice framework for developers to use.

Example Plugin

There is an example plugin found here: Example Project

Framework

Grab the Framework for your project with this:

Add Repositories

Gradle Repository

Kotlin
repositories {
    mavenCentral()
    maven {
        url = uri("https://jitpack.io")
    }
}

Maven Repository

XML
<repositories>
    <repository>
        <id>jitpack</id>
        <url>https://jitpack.io/</url>
    </repository>
</repositories>

Add Dependencies

Gradle Dependency

Kotlin
dependencies {
    compileOnly("com.github.Streamline-Essentials:BukkitOfUtils:master-SNAPSHOT")
}

Maven Dependency

XML
<dependencies>
    <dependency>
        <groupId>com.github.Streamline-Essentials</groupId>
        <artifactId>BukkitOfUtils</artifactId>
        <version>master-SNAPSHOT</version>
    </dependency>
</dependencies>

Framework Usage

The following will help you understand the Framework.

Correct Scheduling (Folia AND Bukkit Support)

Notice: In order to support Folia, we have added a special Universal Scheduler library to the plugin.

Entities and the EntityUtils class
The following is an example of how to use the EntityUtils class to damage entities.

Java
// Pass a boolean value in to indicate if you have called this method on the main Bukkit / Folia thread.
public static void damageAllEntities(boolean isCurrentlySync, double amount) {
    if (isCurrentlySync) {
        // Task is already in sync, so we go ahead and run it.
        damageAllEntitiesInSync(amount);
    } else {
        // Ensure that the task is being run in sync as Bukkit and Folia do not support async entity collecting.
        TaskManager.runTask(() -> {
            damageAllEntitiesInSync(amount);
        });
    }
}

// Only call this method if you are running it on the main thread (hence, "InSync").
public static void damageAllEntitiesInSync(double amount) {
    EntityUtils.collectEntitiesThenDo(entity -> { // For each entity that was collected, do...
        damageEntityInSync(entity, amount);
    });
}

// Only call this method if you are running it on the main thread (hence, "InSync").
public static void damageEntityInSync(Entity entity, double amount) {
    // Have to do this to support Folia.
    TaskManager.runTask(entity, () -> { // Pass the entity to the TaskManager so it can run with Folia support.
        entity.damage(amount);
    });
}

Understanding Why We Do This With Folia
Folia uses regionalized threading for its synchronized tasks. This allows it to be a lot more efficient than Bukkit. BUT, it also means that in order to use an Object that is Region-Specific, without Folia freaking out, you need to run all code that uses that Object in the Object's region thread.

Region-Specific Objects are regionalized by their location (specifically, the chunk they are in). Due to that, anything that has a Location (Bukkit Object Type) will be Region-Specific; including (but not limited to):

  • Entities (Entity Objects)
    • Players (Player Objects)
    • Mobs (LivingEntity, etc. Objects)
    • Etc.
  • Worlds (World Objects)
  • Locations (Location Objects)
  • Blocks (Block Objects)
  • Etc.

Async Task Timers (Repeating and Delayed Timers)

The API supplies a nice Task Timer class.

NOTICE: These Task Timers automatically run asynchronously. So, when you are calling something that needs to be run synchronously, such as anything entity, location, or world-related, you must use the TaskManager class to make them run synchronously (on the Bukkit / Folia main thread).

Basic Repeating Timer
This is a basic repeating timer that runs once every 20 ticks (1 second) and also waits 0 ticks (0 seconds) before running the first time.

Java
public class BasicRepeatingTimer extends BaseRunnable {
    public BasicRepeatingTimer() {
        super(0, 20); // Waits 0 ticks (0 seconds) to run the first time, then runs once every 20 ticks (1 second).
    }

    @Override
    public void run() {
        // Do something here.
        
        // For example...
        DamageHandler.damageAllEntities(false, 1d); // As per our example above. "false" due to it being async (not in sync).
    }
}

Simple Configurations

The following is a simple example configuration using BOU's built-in SimpleConfiguration class.

Java
public class ExampleConfig extends SimpleConfiguration {
    public ExampleConfig(BetterPlugin bouPlugin) {
        super(
            "config.yml", // The name of the file you want to be a configuration. (Will be created automatically.)
            bouPlugin, // Pass the BetterPlugin to it so it knows where to create the file. (You could also pass a File for a folder.)
            false // If the "config.yml" file is embedded into the plugin. (In `resources` folder.)
        );
    }
    
    @Override
    public void init() {
    }
    
    public String getMyLovelyString() {
        reloadConfig(); // Reload the config before getting the value.
    
        // Gets the string at my.lovely.string, or sets it as "BOU is really great!" (in the config file) then returns "BOU is really great!".
        return getOrSetDefault("my.lovely.string", "BOU is really great!");
    }
}

Note about the reloadConfig() method:
This method can be called at any time without causing I/O issues. This is because we cache the file in memory and only write or read from it (on the physical disk) at the minimum of every 5 seconds.

This means that you can call this method at any time without worrying about any I/O issues, such as if you need to query a value from the config file every tick or so.