Updated Developer API (markdown)

Luck 2019-11-18 21:38:49 +00:00
parent 063894060c
commit c90564e85a

@ -1,118 +1,132 @@
### Intro
LuckPerms has a complete developer API, which allows other plugins on the server to read and modify LuckPerms data, and easily integrate LuckPerms deeply into existing plugins and systems.
### Versioning
The API uses [Semantic Versioning](https://semver.org/), meaning whenever a non-backwards compatible change is made, the major version will increment. You can rest assured knowing your integration will not break between versions, providing the major version remains the same.
The current API release is `4.4`.
The current API release is `5.0`.
* The API package in LuckPerms is [`me.lucko.luckperms.api`](https://github.com/lucko/LuckPerms/tree/master/api/src/main/java/me/lucko/luckperms).
* JavaDocs are available either in [a standard JavaDoc layout](https://javadoc.io/doc/me.lucko.luckperms/luckperms-api/), or within the API [source code](https://github.com/lucko/LuckPerms/tree/master/api/src/main/java/me/lucko/luckperms).
* The API package in LuckPerms is `net.luckperms.api`.
* JavaDocs are available either in [a standard JavaDoc layout](https://javadoc.io/doc/me.lucko.luckperms/luckperms-api/), or within the API [source code](https://github.com/lucko/LuckPerms/tree/master/api/src/main/java/net/luckperms/api).
#### Changelogs
* Version `2.x` represented the initial release of the API.
* Version `3.x` (19th Feb 17) introduced a number of backwards incompatible changes. [[changelog](https://gist.github.com/lucko/fdf6ae4b2d9e466d8103dd9c68e5db9e)]
* Version `4.x` (7th Nov 17) introduced a number of backwards incompatible changes. [[changelog](https://gist.github.com/lucko/34c5c3c52ad80f8541395a096a937e91)]
* Version `5.0` was a complete rewrite of the API. Bridging tools are provided to maintain compatibility with older versions.
## Quick start guide
* [Adding LuckPerms to your project](#adding-luckperms-to-your-project)
* [Maven](#maven)
* [Gradle](#gradle)
* [Manual](#manual)
* [Maven](#maven)
* [Gradle](#gradle)
* [Manual](#manual)
* [Obtaining an instance of the API](#obtaining-an-instance-of-the-api)
* [Using the Bukkit ServicesManager](#using-the-bukkit-servicesmanager)
* [Using the Sponge ServicesManager](#using-the-sponge-servicesmanager)
* [Using the singleton](#using-the-singleton-static-access)
* [Using the Bukkit ServicesManager](#using-the-bukkit-servicesmanager)
* [Using the Sponge ServicesManager](#using-the-sponge-servicesmanager)
* [Using the singleton](#using-the-singleton-static-access)
* [Useful information](#useful-information)
* [Thread safety](#thread-safety)
* [Immutability](#immutability)
* [Blocking operations](#blocking-operations)
* [Using CompletableFutures](#using-completablefutures)
* [Asynchronous events & callbacks](#asynchronous-events--callbacks)
* [Thread safety](#thread-safety)
* [Immutability](#immutability)
* [Blocking operations](#blocking-operations)
* [Using CompletableFutures](#using-completablefutures)
* [Asynchronous events & callbacks](#asynchronous-events--callbacks)
___
### Adding LuckPerms to your project
The API artifact is published to the [Maven Central](http://central.sonatype.org/) repository.
#### Maven
If you're using Maven, simply add this to the `dependencies` section of your POM.
````xml
<dependencies>
<dependency>
<groupId>me.lucko.luckperms</groupId>
<groupId>net.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>4.4</version>
<version>5.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
````
#### Gradle
If you're using Gradle, you need to add these lines to your build script.
```gradle
repositories {
mavenCentral()
}
dependencies {
compile 'me.lucko.luckperms:luckperms-api:4.4'
compile 'net.luckperms:luckperms-api:5.0'
}
```
> Note: `compile` can be optionally replaced by either `api` or `implementation` on newer Gradle versions to distinguish whether dependant projects should transitively pick up the LuckPerms API dependency.
#### Manual
If you want to manually add the API dependency to your classpath, you can obtain the jar by:
1. Navigating to [`https://repo1.maven.org/maven2/me/lucko/luckperms/luckperms-api/`](https://repo1.maven.org/maven2/me/lucko/luckperms/luckperms-api/)
1. Navigating to [`https://repo1.maven.org/maven2/net/luckperms/luckperms-api/`](https://repo1.maven.org/maven2/net/luckperms/luckperms-api/)
2. Selecting the version you wish to use
3. Downloading the jar titled `luckperms-api-x.x.jar`
___
### Obtaining an instance of the API
The root API interface is `LuckPermsApi`. You need to obtain an instance of this interface in order to do anything.
The root API interface is `LuckPerms`. You need to obtain an instance of this interface in order to do anything.
It can be obtained in a number of ways.
#### Using the Bukkit ServicesManager
When the plugin is enabled, an instance of `LuckPermsApi` will be provided in the Bukkit ServicesManager. (obviously you need to be writing your plugin for Bukkit!)
When the plugin is enabled, an instance of `LuckPerms` will be provided in the Bukkit ServicesManager. (obviously you need to be writing your plugin for Bukkit!)
```java
RegisteredServiceProvider<LuckPermsApi> provider = Bukkit.getServicesManager().getRegistration(LuckPermsApi.class);
RegisteredServiceProvider<LuckPermsApi> provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class);
if (provider != null) {
LuckPermsApi api = provider.getProvider();
LuckPerms api = provider.getProvider();
}
```
#### Using the Sponge ServicesManager
When the plugin is enabled, an instance of `LuckPermsApi` will be provided in the Sponge ServicesManager. (obviously you need to be writing your plugin for Sponge!)
When the plugin is enabled, an instance of `LuckPerms` will be provided in the Sponge ServicesManager. (obviously you need to be writing your plugin for Sponge!)
```java
Optional<ProviderRegistration<LuckPermsApi>> provider = Sponge.getServiceManager().getRegistration(LuckPermsApi.class);
Optional<ProviderRegistration<LuckPerms>> provider = Sponge.getServiceManager().getRegistration(LuckPerms.class);
if (provider.isPresent()) {
LuckPermsApi api = provider.get().getProvider();
LuckPerms api = provider.get().getProvider();
}
```
#### Using the singleton (static access)
When the plugin is enabled, an instance of `LuckPermsApi` can be obtained statically from the `LuckPerms` class. (this will work on all platforms)
When the plugin is enabled, an instance of `LuckPerms` can be obtained statically from the `LuckPermsProvider` class. (this will work on all platforms)
**Note:** this method will throw an `IllegalStateException` if the API is not loaded.
```java
LuckPermsApi api = LuckPerms.getApi();
LuckPerms api = LuckPermsProvider.get();
```
___
### Useful information
Now you've added the API classes to your project, and obtained an instance of the `LuckPermsApi`, you're almost ready to start using the API. However, before you go any further, please make sure you read and understand the information below.
Now you've added the API classes to your project, and obtained an instance of the `LuckPerms`, you're almost ready to start using the API. However, before you go any further, please make sure you read and understand the information below.
#### Thread safety
@ -140,26 +154,21 @@ This is a super quick guide. If you'd like more comprehensive info, see the [Com
For the purposes of explaining, take the following method in the `ActionLogger` class.
```java
/**
* Gets a {@link Log} instance from the plugin storage.
*
* @return a log instance
*/
CompletableFuture<Log> getLog();
CompletableFuture<ActionLog> getLog();
```
After calling the method, we get a `CompletableFuture<Log>` - the object we actually want is the `Log`. The `CompletableFuture` represents the result of some computation (in this case the computation to obtain the Log), and provides us with methods to obtain the `Log` object.
After calling the method, we get a `CompletableFuture<Log>` - the object we actually want is the `ActionLog`. The `CompletableFuture` represents the result of some computation (in this case the computation to obtain the ActionLog), and provides us with methods to obtain the `ActionLog` object.
If the context of our method call is already asynchronous (if we're calling the method from an async scheduler task), then we can do-away with the future entirely.
```java
/*
Calling this method effectively "requests" a Log from the API.
Calling this method effectively "requests" an ActionLog from the API.
However, it's unlikely that the log will be available immediately...
We need to wait for it to be supplied.
*/
CompletableFuture<Log> logFuture = actionLogger.getLog();
CompletableFuture<ActionLog> logFuture = actionLogger.getLog();
/*
Since we're already on an async thread, it doesn't matter how long we
@ -168,10 +177,10 @@ CompletableFuture<Log> logFuture = actionLogger.getLog();
The #join method will block - and wait until the Log has been supplied,
and then return it.
If for whatever reason the process to obtain a Log threw an exception,
If for whatever reason the process to obtain a ActionLog threw an exception,
this method will rethrow an the same exception wrapped in a CompletionException
*/
Log log = logFuture.join();
ActionLog log = logFuture.join();
```
An alternative to using `#join` is to register a callback with the Future, to be executed once the `Log` is supplied.
@ -183,9 +192,9 @@ If we need to use the instance on the main server thread, then a special executo
Executor executor = runnable -> Bukkit.getScheduler().runTask(this, runnable);
// To be called once the Log is obtained.
logFuture.whenCompleteAsync(new BiConsumer<Log, Throwable>() { // can be reduced to a lambda, I've left it as an anonymous class for clarity
logFuture.whenCompleteAsync(new BiConsumer<ActionLog, Throwable>() { // can be reduced to a lambda, I've left it as an anonymous class for clarity
@Override
public void accept(Log log, Throwable exception) {
public void accept(ActionLog log, Throwable exception) {
if (exception != null) {
// There was some error whilst getting the log.
return;
@ -210,4 +219,3 @@ The CompletableFuture class can initially be very confusing to use (it's still a
* With that in mind, it would be silly to call LuckPerms events synchronously - meaning that, without exception, all events listeners are called asynchronously.
Please keep in mind that many parts of Bukkit, Sponge and the Minecraft server in general are not thread-safe, and should only be interacted with from the server thread. If you need to use Bukkit or Sponge methods from within LuckPerms event listeners or callbacks, you need to perform your action using the scheduler.