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
repositories {
mavenCentral()
maven {
url = uri("https://jitpack.io")
}
}
Maven Repository
<repositories>
<repository>
<id>jitpack</id>
<url>https://jitpack.io/</url>
</repository>
</repositories>
Add Dependencies
Gradle Dependency
dependencies {
compileOnly("com.github.Streamline-Essentials:BukkitOfUtils:master-SNAPSHOT")
}
Maven Dependency
<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.
// 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.
- Players (
- 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.
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.
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.