Additional documentation

This commit is contained in:
Paul Reilly 2023-06-09 13:23:06 -05:00
parent 1ea106d999
commit 09c511ef34
10 changed files with 469 additions and 48 deletions

View File

@ -1,7 +1,6 @@
package me.totalfreedom.display;
import me.totalfreedom.service.Task;
import net.kyori.adventure.audience.Audience;
import org.bukkit.Bukkit;
import java.time.Duration;

View File

@ -2,17 +2,40 @@ package me.totalfreedom.security;
import net.kyori.adventure.text.Component;
/**
* Represents a permissible group which holds a set of permissions that can then be applied to a User / Player.
*/
public interface Group extends PermissionHolder
{
/**
* @return The name of the group.
*/
Component getName();
/**
* @return The prefix of the group.
*/
Component getPrefix();
/**
* @return The abbreviation of the group.
*/
Component getAbbreviation();
/**
* @return The weight of the group.
*/
int getWeight();
/**
* If more than one group is marked as default, the first retrieved default group will be used.
*
* @return Whether this is the default group.
*/
boolean isDefault();
/**
* @return Whether the group is hidden.
*/
boolean isHidden();
}

View File

@ -4,13 +4,28 @@ import me.totalfreedom.base.CommonsBase;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* This is a holder class for {@link Executor} objects that are used to delegate runnable tasks to the Bukkit Scheduler.
* This class is here for both convenience purposes, and also for the sake of providing easy access to executors for
* {@link CompletableFuture} invocations.
*/
public class FreedomExecutor
{
/**
* An executor which runs tasks synchronously.
*/
private final Executor syncExecutor;
/**
* An executor which runs tasks asynchronously.
*/
private final Executor asyncExecutor;
/**
* Creates a new {@link FreedomExecutor} instance.
*/
public FreedomExecutor()
{
syncExecutor = r -> Bukkit.getScheduler()
@ -19,47 +34,107 @@ public class FreedomExecutor
.runTaskAsynchronously(CommonsBase.getInstance(), r);
}
/**
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, synchronously.
*
* @param plugin The plugin to run the task for.
* @return A new {@link Executor} instance.
*/
public Executor singleExecutor(final JavaPlugin plugin)
{
return r -> Bukkit.getScheduler()
.runTask(plugin, r);
}
/**
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, synchronously. This
* Executor will wait for the supplied delay before executing the runnable.
*
* @param plugin The plugin to run the task for.
* @param delay The delay to wait before executing the runnable.
* @return A new {@link Executor} instance.
*/
public Executor delayedExecutor(final JavaPlugin plugin, final long delay)
{
return r -> Bukkit.getScheduler()
.runTaskLater(plugin, r, delay);
}
public Executor periodicExecutor(final JavaPlugin plugin, final long delay, final long period)
/**
* Creates a new {@link Executor} tthat is capable of executing a runnable on a periodic basis, synchronously. This
* executor can also be supplied a delay to indicate it should wait the specified amount of time before executing
* the runnable for the first time.
*
* @param plugin The plugin to run the task for.
* @param initialDelay The delay to wait before executing the runnable for the first time.
* @param period The period to wait between each execution of the runnable.
* @return A new {@link Executor} instance.
*/
public Executor periodicExecutor(final JavaPlugin plugin, final long initialDelay, final long period)
{
return r -> Bukkit.getScheduler()
.runTaskTimer(plugin, r, delay, period);
.runTaskTimer(plugin, r, initialDelay, period);
}
/**
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, asynchronously.
*
* @param plugin The plugin to run the task for.
* @return A new {@link Executor} instance.
*/
public Executor asynchronousSingleExecutor(final JavaPlugin plugin)
{
return r -> Bukkit.getScheduler()
.runTaskAsynchronously(plugin, r);
}
/**
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, asynchronously. This
* Executor will wait for the supplied delay before executing the runnable.
*
* @param plugin The plugin to run the task for.
* @param delay The delay to wait before executing the runnable.
* @return A new {@link Executor} instance.
*/
public Executor asynchronousDelayedExecutor(final JavaPlugin plugin, final long delay)
{
return r -> Bukkit.getScheduler()
.runTaskLaterAsynchronously(plugin, r, delay);
}
/**
* Creates a new {@link Executor} tthat is capable of executing a runnable on a periodic basis, asynchronously. This
* executor can also be supplied a delay to indicate it should wait the specified amount of time before executing
* the runnable for the first time.
*
* @param plugin The plugin to run the task for.
* @param delay The delay to wait before executing the runnable for the first time.
* @param period The period to wait between each execution of the runnable.
* @return A new {@link Executor} instance.
*/
public Executor asynchronousPeriodicExecutor(final JavaPlugin plugin, final long delay, final long period)
{
return r -> Bukkit.getScheduler()
.runTaskTimerAsynchronously(plugin, r, delay, period);
}
/**
* Gets the synchronous executor instance. This is a convenience for {@link CompletableFuture} invocations,
* when defining a custom executor for the {@link CompletableFuture}.
*
* @return The synchronous executor instance.
*/
public Executor getSync()
{
return syncExecutor;
}
/**
* Gets the asynchronous executor instance. This is a convenience for {@link CompletableFuture} invocations,
* when defining a custom executor for the {@link CompletableFuture}.
*
* @return The asynchronous executor instance.
*/
public Executor getAsync()
{
return asyncExecutor;

View File

@ -1,16 +1,45 @@
package me.totalfreedom.service;
/**
* Represents a ticking service. Services may be asynchronous or synchronous, however there are some restrictions:
* <ul>
* <li>Sync services may not have a complexity greater than 5.</li>
* <li>Async services may not interact with the Bukkit API in any form.</li>
* </ul>
*/
public abstract class Service
{
/**
* The name of the service.
*/
private final String name;
/**
* Creates a new service with the given name.
*
* @param name The name of the service.
*/
protected Service(final String name)
{
this.name = name;
}
/**
* This method is called every single tick, or at least, every tick interval described by the holding
* {@link ServiceSubscription}. Since this runs every single tick, the body of this method should not have a
* complexity higher than 5. This includes Cyclomatic, Cognitive, and NPath complexities. If the service is
* asynchronous, there is a bit more flexibility with the complexity rating, extending to no more than 10. However,
* it's generally good practice to keep the complexity of ticking services as low as possible to avoid extensive
* resource consumption.
*
* @see ServiceSubscription
* @see SubscriptionProvider
*/
public abstract void tick();
/**
* @return The name of the service.
*/
public String getName()
{
return name;

View File

@ -7,32 +7,105 @@ import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executor;
/**
* Represents a subscription to a {@link Service}.
* <p>
* Subscriptions contain some information about the service itself and it's presence on the scheduler. For example,
* {@link #getServiceId()} will return the ID of the task which was returned by the scheduler. Subscriptions also manage
* the state of the service, using {@link #isActive()} to determine if the service is currently running.
* <br>
* <br>
* The subscription itself provides type inference to safely store the actual service instance. This is useful for when
* we need to access the service itself, without calling to the service directly.
*
* @param <T> The type of service this subscription is for.
*/
public final class ServiceSubscription<T extends Service>
{
/**
* The service this subscription is for.
*/
private final T service;
/**
* Whether this is an asynchronous service.
*/
private final boolean async;
/**
* The executor used to schedule the service.
*/
private final Executor executor;
/**
* The ID of the service from the associated {@link BukkitTask} which was returned by the Scheduler.
*/
private final int serviceId;
/**
* Whether the service is currently running.
*/
private boolean isActive = false;
/**
* Creates a new subscription for the given service. By default, this method will mark this service as a synchronous
* service. This will also initialize the default interval to a single tick.
* <br>
* If you are trying to create an asynchronous service, use
* {@link #ServiceSubscription(JavaPlugin, Service, boolean)} instead.
* <br>
* If you would like to define a custom interval, use either {@link #ServiceSubscription(JavaPlugin, Service, long)}
* or {@link #ServiceSubscription(JavaPlugin, Service, long, boolean)} (for asynchronous services).
*
* @param plugin The plugin which owns the service.
* @param service The service to subscribe to.
*/
ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service)
{
this(plugin, service, 1L, false);
}
/**
* Creates a new subscription for the given service. This will initialize the default interval to a single tick.
* <br>
* If you would like to define a custom interval, use either {@link #ServiceSubscription(JavaPlugin, Service, long)}
* or {@link #ServiceSubscription(JavaPlugin, Service, long, boolean)} (for asynchronous services).
*
* @param plugin The plugin which owns the service.
* @param service The service to subscribe to.
* @param async Whether the service should be scheduled asynchronously.
*/
ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service, final boolean async)
{
this(plugin, service, 1L, async);
}
/**
* Creates a new subscription for the given service. By default, this will mark the service as synchronous. When
* defining a custom interval, the interval should be less than 20L (the number of ticks in a second). For anything
* that requires an interval greater than 1 second, use a {@link Task} instead.
* <br>
* If you are trying to create an asynchronous service, use
* {@link #ServiceSubscription(JavaPlugin, Service, long, boolean)} instead.
*
* @param plugin The plugin which owns the service.
* @param service The service to subscribe to.
* @param interval The interval at which the service should be scheduled.
*/
ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service, final long interval)
{
this(plugin, service, interval, false);
}
/**
* Creates a new subscription for the given service. When defining a custom interval, the interval should be less
* than 20L (the number of ticks in a second). For anything that requires an interval greater than 1 second, use a
* {@link Task} instead.
*
* @param plugin The plugin which owns the service.
* @param service The service to subscribe to.
* @param interval The interval at which the service should be scheduled.
* @param async Whether the service should be scheduled asynchronously.
*/
ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service,
final long interval, final boolean async)
final long interval, final boolean async)
{
this.service = service;
this.async = async;
@ -60,12 +133,18 @@ public final class ServiceSubscription<T extends Service>
this.serviceId = tempId[0];
}
/**
* Starts the service.
*/
public void start()
{
this.isActive = true;
this.executor.execute(service::tick);
}
/**
* Stops the service.
*/
public void stop()
{
this.isActive = false;
@ -73,22 +152,36 @@ public final class ServiceSubscription<T extends Service>
.cancelTask(this.getServiceId());
}
/**
* Returns the ID of the service from the associated {@link BukkitTask} which was returned by the Scheduler.
*
* @return The ID of the service.
*/
public int getServiceId()
{
return serviceId;
}
/**
* @return The service this subscription is for.
*/
@NotNull
public T getService()
{
return service;
}
/**
* @return Whether this is an asynchronous service.
*/
public boolean isAsync()
{
return async;
}
/**
* @return Whether the service is currently running.
*/
public boolean isActive()
{
return isActive;

View File

@ -4,59 +4,121 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
/**
* Provides static methods for creating {@link ServiceSubscription} and {@link TaskSubscription} objects.
*/
public final class SubscriptionProvider
{
/**
* Prevents instantiation of this class.
*/
private SubscriptionProvider()
{
throw new AssertionError();
}
/**
* Creates a new {@link ServiceSubscription} object that will run the given {@link Service} object on the main
* thread a single time.
*
* @param plugin The plugin that owns the service.
* @param service The service to run.
* @param <S> Type inference to maintain the service type.
* @return The new {@link ServiceSubscription} object.
*/
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final <S extends Service> ServiceSubscription<S> syncService(@NotNull final JavaPlugin plugin,
@NotNull final S service)
@NotNull final S service)
{
return new ServiceSubscription<>(plugin, service);
}
/**
* Creates a new {@link ServiceSubscription} object that will run the given {@link Service} object on the main
* thread at the given interval.
*
* @param plugin The plugin that owns the service.
* @param interval The interval to run the service at.
* @param service The service to run.
* @param <S> Type inference to maintain the service type.
* @return The new {@link ServiceSubscription} object.
*/
@NotNull
@Contract(value = "_,_,_ -> new", pure = false)
public static final <S extends Service> ServiceSubscription<S> syncService(@NotNull final JavaPlugin plugin,
final long interval,
@NotNull final S service)
final long interval,
@NotNull final S service)
{
return new ServiceSubscription<>(plugin, service, interval);
}
/**
* Creates a new {@link ServiceSubscription} object that will run the given {@link Service} object on the default
* tick interval, which is a single tick. This method will create an asynchronous service.
*
* @param plugin The plugin that owns the service.
* @param service The service to run.
* @param <S> Type inference to maintain the service type.
* @return The new {@link ServiceSubscription} object.
*/
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final <S extends Service> ServiceSubscription<S> asyncService(@NotNull final JavaPlugin plugin,
@NotNull final S service)
@NotNull final S service)
{
return new ServiceSubscription<>(plugin, service, true);
}
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final <T extends Task> TaskSubscription<T> runSyncTask(@NotNull final JavaPlugin plugin,
@NotNull final T task)
{
return new TaskSubscription<>(plugin, task, false);
}
/**
* Creates a new {@link ServiceSubscription} object that will run the given {@link Service} object on the given
* interval. This method will create an asynchronous service.
*
* @param plugin The plugin that owns the service.
* @param interval The interval to run the service at.
* @param service The service to run.
* @param <S> Type inference to maintain the service type.
* @return The new {@link ServiceSubscription} object.
*/
@NotNull
@Contract(value = "_,_,_ -> new", pure = false)
public static final <S extends Service> ServiceSubscription<S> asyncService(@NotNull final JavaPlugin plugin,
final long interval,
@NotNull final S service)
final long interval,
@NotNull final S service)
{
return new ServiceSubscription<>(plugin, service, interval, true);
}
/**
* Creates a new {@link TaskSubscription} object that will run the given {@link Task} object synchronously on the
* main thread.
*
* @param plugin The plugin that owns the task.
* @param task The task to run.
* @param <T> Type inference to maintain the task type.
* @return The new {@link TaskSubscription} object.
*/
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final <T extends Task> TaskSubscription<T> runSyncTask(@NotNull final JavaPlugin plugin,
@NotNull final T task)
{
return new TaskSubscription<>(plugin, task, false);
}
/**
* Creates a new {@link TaskSubscription} object that will run the given {@link Task} object asynchronously on the
* main thread.
*
* @param plugin The plugin that owns the task.
* @param task The task to run.
* @param <T> Type inference to maintain the task type.
* @return The new {@link TaskSubscription} object.
*/
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final <T extends Task> TaskSubscription<T> runAsyncTask(@NotNull final JavaPlugin plugin,
@NotNull final T task)
@NotNull final T task)
{
return new TaskSubscription<>(plugin, task, true);
}

View File

@ -5,17 +5,45 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.time.Duration;
/**
* Represents a task that can be run asynchronously or synchronously.
*/
public abstract class Task extends BukkitRunnable
{
/**
* The name of the task.
*/
private final String name;
private long delay;
private long interval;
/**
* The delay of the task.
*/
private final long delay;
/**
* The interval of the task.
*/
private final long interval;
/**
* Creates a new task with the given name. This will initialize a task with no initail delay and no interval.
*
* @param name The name of the task.
*/
protected Task(final String name)
{
this(name, -1L, -1L);
}
/**
* Creates a new task with the given name, delay, and interval.
* <br>
* It's important to note that the delay and interval are in ticks. One tick is equal to 1/20th of a second, which
* means there are 20 ticks are in one second. If your interval is intended to be anything less than 20 ticks, you
* should use a {@link Service} instead.
*
* @param name The name of the task.
* @param delay The delay of the task.
* @param interval The interval of the task.
*/
protected Task(final String name, final long delay, final long interval)
{
this.name = name;
@ -23,61 +51,102 @@ public abstract class Task extends BukkitRunnable
this.interval = interval;
}
/**
* Creates a new task with the given name and delay. This will intialize a single execute task with an
* initial delay before execution.
*
* @param name The name of the task.
* @param delay How long the task should wait before executing.
*/
protected Task(final String name, final long delay)
{
this(name, delay, -1L);
}
/**
* Creates a new task with the given name and delay.
* This is the same as longs, except that here, we naturally support durations which are automatically converted to
* ticks for you. This means that using {@link Duration#ofSeconds(long)} will work as expected.
*
* @param name The name of the task.
* @param delay How long the task should wait before executing.
*/
protected Task(final String name, final Duration delay)
{
this(name, DurationTools.getTickedSeconds(delay), -1L);
}
/**
* Creates a new task with the given name, delay, and interval.
* This is the same as longs, except that here, we naturally support durations which are automatically converted to
* ticks for you. This means that using {@link Duration#ofSeconds(long)} will work as expected.
*
* @param name The name of the task.
* @param delay How long the task should wait before executing.
* @param interval How long the task should wait between executions.
*/
protected Task(final String name, final Duration delay, final Duration interval)
{
this(name, DurationTools.getTickedSeconds(delay), DurationTools.getTickedSeconds(interval));
}
/**
* Creates a new task with the given name, delay, and interval.
* This method is a convenience method to use a {@link Duration} for the interval, while also being able to
* specify the delay as -1L so the task does not have an initial delay before execution.
*
* @param name The name of the task.
* @param delay The delay of the task.
* @param interval The interval of the task.
*/
protected Task(final String name, final long delay, final Duration interval)
{
this(name, delay, DurationTools.getTickedSeconds(interval));
}
/**
* @return True if the task is running, false otherwise.
*/
public boolean isRunning()
{
return !isCancelled();
}
/**
* @return The name of the task.
*/
public String getName()
{
return name;
}
/**
* @return True if the task is repeating, false otherwise.
*/
public boolean isRepeating()
{
return interval != -1L;
}
public void setRepeating(final long interval)
{
this.interval = interval;
return this.interval > 0L;
}
/**
* @return True if the task is delayed, false otherwise.
*/
public boolean isDelayed()
{
return this.delay != -1;
}
public void setDelayed(final long delay)
{
this.delay = delay;
return this.delay > 0L;
}
/**
* @return The interval between each task execution.
*/
public long getInterval()
{
return interval;
}
/**
* @return The initial delay before the first execution of this task.
*/
public long getDelay()
{
return delay;

View File

@ -3,39 +3,82 @@ package me.totalfreedom.service;
import me.totalfreedom.utils.Pair;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.concurrent.Executor;
/**
* Represents a subscription to a task. Task subscriptions offer a nice wrapper for managing tasks, which are
* inevitably just bukkit runnables with a bit more lenience in terms of instantiation modification and execution.
* It also offers a more intuitive way to manage our tasks; rather than having to keep track of task ids for each
* {@link BukkitTask} object that gets returned by the {@link BukkitScheduler}.
*
* @param <T> The type of task.
*/
public final class TaskSubscription<T extends Task>
{
/**
* The task that is being subscribed to.
*/
private final T task;
/**
* The task id of the task.
*/
private final int taskId;
/**
* True if the task is async, false otherwise.
*/
private final boolean async;
/**
* The executor that will execute the task.
*/
private final Executor executor;
/**
* True if the task is active, false otherwise. By default, this is set to false, and will be marked as true when
* the task is started.
*/
private boolean isActive = false;
/**
* Creates a new task subscription.
*
* @param plugin The plugin which owns the task.
* @param task The task that is being subscribed to.
* @param async True if the task is async, false otherwise.
*/
TaskSubscription(final JavaPlugin plugin, final T task, final boolean async)
{
this.task = task;
this.async = async;
final long delay = (task.isDelayed()
? task.getDelay()
: 0);
? task.getDelay()
: 0);
final long period = (task.isRepeating()
? task.getInterval()
: 0);
? task.getInterval()
: 0);
final Pair<Integer, Executor> integerExecutorPair = async
? getAsync(plugin, delay, period)
: getSync(plugin, delay, period);
? getAsync(plugin, delay, period)
: getSync(plugin, delay, period);
this.executor = integerExecutorPair.value();
this.taskId = integerExecutorPair.key();
}
/**
* Gets the executor and task id for an async task, wrapped in a {@link Pair}&lt;{@link Integer},
* {@link Executor}&gt;.
* <br>
* This will return a Pair where {@link Pair#value()} is an asynchronous executor.
*
* @param plugin The plugin which owns the task.
* @param delay The delay of the task.
* @param period The period of the task.
* @return The executor and task id for an asynchronous task.
*/
private Pair<Integer, Executor> getAsync(final JavaPlugin plugin, final long delay, final long period)
{
final Executor executor1;
@ -69,6 +112,17 @@ public final class TaskSubscription<T extends Task>
return new Pair<>(tempId[0], executor1);
}
/**
* Gets the executor and task id for a sync task, wrapped in a {@link Pair}&lt;{@link Integer},
* {@link Executor}&gt;.
* <br>
* This will return a Pair where {@link Pair#value()} is a synchronous executor.
*
* @param plugin The plugin which owns the task.
* @param delay The delay of the task.
* @param period The period of the task.
* @return The executor and task id for a synchronous task.
*/
private Pair<Integer, Executor> getSync(final JavaPlugin plugin, final long delay, final long period)
{
final Executor executor1;
@ -103,12 +157,18 @@ public final class TaskSubscription<T extends Task>
return new Pair<>(tempId[0], executor1);
}
/**
* Starts the task.
*/
public void start()
{
this.isActive = true;
executor.execute(task);
}
/**
* Stops the task.
*/
public void stop()
{
this.isActive = false;
@ -116,26 +176,41 @@ public final class TaskSubscription<T extends Task>
.cancelTask(this.getTaskId());
}
/**
* @return The task id of the task.
*/
public int getTaskId()
{
return taskId;
}
/**
* @return The task that is being subscribed to.
*/
public T getTask()
{
return task;
}
/**
* @return True if the task is async, false otherwise.
*/
public boolean isAsync()
{
return async;
}
/**
* @return The executor that will execute the task.
*/
public Executor getExecutor()
{
return executor;
}
/**
* @return True if the task is active, false otherwise.
*/
public boolean isActive()
{
return isActive;

View File

@ -1,13 +1,10 @@
package me.totalfreedom.shop;
import me.totalfreedom.display.BossBarDisplay;
import me.totalfreedom.economy.EconomicEntity;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import org.bukkit.event.Listener;
import java.time.Duration;
import java.util.function.Consumer;
public interface Reactable
{

View File

@ -1,7 +1,6 @@
package me.totalfreedom.shop;
import me.totalfreedom.display.BossBarDisplay;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import java.time.Duration;
@ -44,6 +43,11 @@ public abstract class Reaction implements Reactable
return reactionMessage;
}
public void setReactionMessage(final Component message)
{
this.reactionMessage = message;
}
@Override
public Duration getReactionDuration()
{
@ -61,9 +65,4 @@ public abstract class Reaction implements Reactable
{
}
public void setReactionMessage(final Component message)
{
this.reactionMessage = message;
}
}