diff --git a/Datura/src/main/java/me/totalfreedom/datura/Datura.java b/Datura/src/main/java/me/totalfreedom/datura/Datura.java
index 5ef1440..da40da4 100644
--- a/Datura/src/main/java/me/totalfreedom/datura/Datura.java
+++ b/Datura/src/main/java/me/totalfreedom/datura/Datura.java
@@ -27,11 +27,11 @@ public class Datura extends JavaPlugin
CommonsBase.getInstance()
.getRegistrations()
- .getServiceRegistry()
+ .getServiceTaskRegistry()
.registerService(SubscriptionProvider.syncService(this, locker));
CommonsBase.getInstance()
.getRegistrations()
- .getServiceRegistry()
+ .getServiceTaskRegistry()
.registerService(SubscriptionProvider.syncService(this, cager));
Bukkit.getPluginManager()
diff --git a/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java
index cdd00f1..50a716b 100644
--- a/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java
+++ b/Datura/src/main/java/me/totalfreedom/datura/perms/FreedomUser.java
@@ -45,7 +45,7 @@ public class FreedomUser implements User
final Datura datura = CommonsBase.getInstance()
.getRegistrations()
.getModuleRegistry()
- .getModule(Datura.class)
+ .getProvider(Datura.class)
.getModule();
UserData data = SimpleUserData.fromSQL(datura.getSQL(), uuid.toString());
diff --git a/Datura/src/main/java/me/totalfreedom/datura/user/ServerEconomyHolder.java b/Datura/src/main/java/me/totalfreedom/datura/user/ServerEconomyHolder.java
index 6db83ee..e9771f3 100644
--- a/Datura/src/main/java/me/totalfreedom/datura/user/ServerEconomyHolder.java
+++ b/Datura/src/main/java/me/totalfreedom/datura/user/ServerEconomyHolder.java
@@ -3,40 +3,112 @@ package me.totalfreedom.datura.user;
import me.totalfreedom.economy.EconomicEntity;
import me.totalfreedom.economy.EconomicEntityData;
+/**
+ * Represents the server's economy holder.
+ *
+ *
+ * This is effectively a Bank object which is meant to represent the server itself,
+ * which can store a balance and perform transactions with other EconomicEntity objects.
+ *
+ *
+ * The server is initially given a maximum balance of {@link Long#MAX_VALUE}, though this can be changed
+ * using the constructor {@link #ServerEconomyHolder(String, long)}. The value that this
+ * bank object holds is persistent, which means that the total economic resources available
+ * are of limited supply.
+ *
+ *
+ * Please be aware, if the server's economy falls below 0,
+ * it will have drastic consequences.
+ */
public class ServerEconomyHolder implements EconomicEntity, EconomicEntityData
{
- private long balance = Long.MAX_VALUE;
+ private final String name;
+ private long balance;
+ /**
+ * Constructs a new ServerEconomyHolder with the specified name and a balance of {@link Long#MAX_VALUE}.
+ *
+ * @param name The name of this server economy holder.
+ */
+ public ServerEconomyHolder(final String name)
+ {
+ this.name = name;
+ this.balance = Long.MAX_VALUE;
+ }
+
+ /**
+ * Constructs a new ServerEconomyHolder with the specified name and balance.
+ *
+ * @param name The name of this server economy holder.
+ * @param balance The balance of this server economy holder.
+ */
+ public ServerEconomyHolder(final String name, final long balance)
+ {
+ this.name = name;
+ this.balance = balance;
+ }
+
+ /**
+ * This method will return this object, as it is both the EconomicEntity and the EconomicEntityData.
+ * This is due to the fact that the server should only ever have one singular concrete representation
+ * of it's economic entity and the respective data.
+ *
+ * @return this object.
+ */
@Override
public EconomicEntityData getEconomicData()
{
return this;
}
+ /**
+ * @return The name of this server economy holder.
+ */
@Override
public String getName()
{
- return "TotalFreedom-Bank";
+ return name;
}
+ /**
+ * This method will always return false, as the server should not ever be
+ * prevented from performing transactions.
+ *
+ * @return false
+ */
@Override
public boolean areTransactionsFrozen()
{
return false;
}
+ /**
+ * @return The server's current available balance.
+ */
@Override
public long getBalance()
{
return balance;
}
+ /**
+ * Sets the server's balance to the specified value.
+ *
+ * @param newBalance The new balance to set.
+ */
@Override
public void setBalance(final long newBalance)
{
balance = newBalance;
}
+ /**
+ * Adds the specified amount to the server's balance.
+ * This method mutates the balance and returns the new balance.
+ *
+ * @param amount The amount to add.
+ * @return The new balance.
+ */
@Override
public long addToBalance(final long amount)
{
@@ -44,6 +116,13 @@ public class ServerEconomyHolder implements EconomicEntity, EconomicEntityData
return balance;
}
+ /**
+ * Removes the specified amount from the server's balance.
+ * This method mutates the balance and returns the new balance.
+ *
+ * @param amount The amount to remove.
+ * @return The new balance.
+ */
@Override
public long removeFromBalance(final long amount)
{
diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/Fossil.java b/Fossil/src/main/java/me/totalfreedom/fossil/Fossil.java
index a638249..8d789f8 100644
--- a/Fossil/src/main/java/me/totalfreedom/fossil/Fossil.java
+++ b/Fossil/src/main/java/me/totalfreedom/fossil/Fossil.java
@@ -18,7 +18,7 @@ public class Fossil extends JavaPlugin
registration.getModuleRegistry()
.addModule(this);
- registration.getServiceRegistry()
+ registration.getServiceTaskRegistry()
.registerService(
SubscriptionProvider.syncService(this, trailer));
}
diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/BouncyPad.java b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/BouncyPad.java
new file mode 100644
index 0000000..cacd37a
--- /dev/null
+++ b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/BouncyPad.java
@@ -0,0 +1,211 @@
+package me.totalfreedom.fossil.bouncypads;
+
+import com.google.errorprone.annotations.Immutable;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import java.util.SplittableRandom;
+
+/**
+ * Represents a bouncy pad. Has a velocity and a type.
+ */
+@Immutable
+public class BouncyPad
+{
+ /**
+ * The velocity of the pad.
+ */
+ private final double velocity;
+ /**
+ * The type of the pad.
+ */
+ private final PadType padType;
+
+ /**
+ * Creates a new bouncy pad.
+ *
+ * @param velocity The velocity of the pad.
+ * @param padType The type of the pad.
+ */
+ public BouncyPad(final double velocity, final PadType padType)
+ {
+ this.velocity = velocity;
+ this.padType = padType;
+ }
+
+ /**
+ * Creates a new bouncy pad with a type of {@link PadType#NORMAL}.
+ *
+ * @param velocity The velocity of the pad.
+ */
+ public BouncyPad(final double velocity)
+ {
+ this(velocity, PadType.NORMAL);
+ }
+
+ /**
+ * Creates a new bouncy pad with a velocity of 1.1 and a type of {@link PadType#NORMAL}.
+ */
+ public BouncyPad()
+ {
+ this(1.0 + 0.1F);
+ }
+
+ /**
+ * This method will bounce the player based on the type of the pad.
+ *
+ * The type of the pad, defined by {@link #padType}, will determine how the player is bounced.
+ *
+ * For type {@link PadType#NORMAL}, the player will be bounced if the face is {@link BlockFace#UP}.
+ *
+ * For type {@link PadType#SIDES}, the player will be bounced if the face is not {@link BlockFace#UP} or
+ * {@link BlockFace#DOWN}.
+ *
+ * For type {@link PadType#ALL}, the player will be bounced regardless of the face.
+ *
+ * For type {@link PadType#EXTREME}, the player will be bounced with a velocity based on the formula:
+ *
+ * (((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 *
+ * Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1))
+ *
+ * For type {@link PadType#SPACE_CADET}, the player will be bounced with a velocity based on the formula:
+ *
+ * Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
+ * Math.exp(accel)))
+ *
+ * where y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)
and accel = Math.sqrt(2 * 9.81 * y)
+ *
+ *
+ * NOTE: The velocity of the pad is added with the inverse velocity of the player. The inverse
+ * velocity of the player is acquired by multiplying the velocity of the player by -1.
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ public void bouncePad(final Player player, final BlockFace face)
+ {
+ switch (padType)
+ {
+ case NORMAL -> bounceNormal(player, face);
+ case SIDES -> bounceSides(player, face);
+ case ALL -> bounceAll(player, face);
+ case EXTREME -> bounceExtreme(player, face);
+ case SPACE_CADET -> bounceSpaceCadet(player, face);
+ }
+ }
+
+ /**
+ * This method returns a vector based on the following:
+ *
+ * (BlockFace direction + Player velocity * -1) * velocity
+ *
+ *
+ * We retrieve a vector representing the direction in which this block face is facing. This is then added with the
+ * inverse velocity of the player, which is the direction and speed in which the player is moving multiplied by -1.
+ * This is then multiplied by the velocity of the pad.
+ *
+ * @param player The moving player
+ * @param face The face of the block the player is bouncing on.
+ * @return A vector representing the direction and speed in which the player should be bounced.
+ */
+ private Vector getVector(final Player player, final BlockFace face)
+ {
+ return face.getDirection()
+ .add(player.getVelocity()
+ .multiply(-1))
+ .multiply(velocity);
+ }
+
+ /**
+ * This method will bounce the player if the face is {@link BlockFace#UP}.
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ private void bounceNormal(final Player player, final BlockFace face)
+ {
+ if (!face.equals(BlockFace.UP))
+ return;
+
+ player.setVelocity(getVector(player, face));
+ }
+
+ /**
+ * This method will bounce the player if the face is not {@link BlockFace#UP} or {@link BlockFace#DOWN}.
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ private void bounceSides(final Player player, final BlockFace face)
+ {
+ if (face == BlockFace.UP || face == BlockFace.DOWN)
+ return;
+
+ player.setVelocity(getVector(player, face));
+ }
+
+ /**
+ * This method will bounce the player regardless of the face.
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ private void bounceAll(final Player player, final BlockFace face)
+ {
+ player.setVelocity(getVector(player, face));
+ }
+
+ /**
+ * This method will bounce the player with a velocity based on the formula:
+ *
+ * (((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 *
+ * Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1))
+ *
+ *
+ * NOTE: The velocity of the pad is added with the inverse velocity of the player. The inverse
+ * velocity of the player is acquired by multiplying the velocity of the player by -1.
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ private void bounceExtreme(final Player player, final BlockFace face)
+ {
+ final double extremeVelocity = (((173.31 + 0.5 * velocity) - (31.2 + 0.5 * Math.pow(velocity, 2.0)) + (0.5 * Math.pow(velocity, 3.0))) - 173.31) / (velocity * (velocity - 1));
+ player.setVelocity(face.getDirection()
+ .add(player.getVelocity()
+ .multiply(-1))
+ .multiply(extremeVelocity * velocity));
+ }
+
+ /**
+ * This method will bounce the player with a velocity based on the formula:
+ *
+ * Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
+ * Math.exp(accel)))
+ *
+ * where y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)
and
+ * accel = Math.sqrt(2 * 9.81 * y)
+ *
+ * @param player The player to bounce.
+ * @param face The face of the block the player is bouncing on.
+ */
+ private void bounceSpaceCadet(final Player player, final BlockFace face)
+ {
+ final SplittableRandom random = new SplittableRandom();
+ final double y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5);
+ final double accel = Math.sqrt(2 * 9.81 * y);
+ final double spaceVelocity = Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) / Math.exp(accel)));
+
+ final Vector accelVector = new Vector(0, y + accel, 0);
+ final Vector postVector = new Vector(0, spaceVelocity, 0);
+
+ final Vector spaceVector = face.getDirection()
+ .add(player.getVelocity()
+ .multiply(-1))
+ .multiply(accelVector.multiply(postVector));
+
+ player.setVelocity(spaceVector);
+ }
+}
diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadHolder.java b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadHolder.java
new file mode 100644
index 0000000..829739d
--- /dev/null
+++ b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadHolder.java
@@ -0,0 +1,156 @@
+package me.totalfreedom.fossil.bouncypads;
+
+import me.totalfreedom.base.CommonsBase;
+import me.totalfreedom.fossil.Fossil;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Tag;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+/**
+ * Holds all the active pads for each player, and also manages player pad interaction.
+ */
+public class PadHolder implements Listener
+{
+ /**
+ * A map of all the currently active pads, stored by {@link Player} {@link UUID}.
+ */
+ private final Map pads = new HashMap<>();
+
+ /**
+ * Creates a new pad holder.
+ */
+ public PadHolder()
+ {
+ Bukkit.getPluginManager().registerEvents(this, CommonsBase
+ .getInstance()
+ .getRegistrations()
+ .getModuleRegistry()
+ .getProvider(Fossil.class)
+ .getModule());
+ }
+
+ /**
+ * Adds a pad for the given player. If the player already has a pad stored in the map,
+ * it will be overwritten with the new pad.
+ *
+ * @param player The player to add the pad for.
+ * @param pad The pad to add.
+ */
+ public void addPad(final Player player, final BouncyPad pad)
+ {
+ this.pads.put(player.getUniqueId(), pad);
+ }
+
+ /**
+ * Removes the pad for the given player, if the player has one.
+ *
+ * @param player The player to remove the pad for.
+ */
+ public void removePad(final Player player)
+ {
+ this.pads.remove(player.getUniqueId());
+ }
+
+ /**
+ * Gets the pad for the given player, if the player has one.
+ * If the player has no active pad, this will return null.
+ *
+ * @param player The player to get the pad for.
+ * @return The pad for the given player.
+ */
+ @Nullable
+ public BouncyPad getPad(final Player player)
+ {
+ return this.pads.get(player.getUniqueId());
+ }
+
+ /**
+ * Checks if there is a pad active for the given player.
+ *
+ * @param player The player to check.
+ * @return True if the player has a pad, false otherwise.
+ */
+ public boolean hasPad(final Player player)
+ {
+ return this.pads.containsKey(player.getUniqueId());
+ }
+
+ /**
+ * Gets a map of all the currently active pads, stored by {@link Player} {@link UUID}.
+ *
+ * @return A map of all the currently active pads.
+ */
+ public Map getPads()
+ {
+ return this.pads;
+ }
+
+ /**
+ * Handles player pad interaction. This will check the relative block for each acceptible direction, and pass the
+ * resulting block face (if any) to the bounce pad. See {@link BouncyPad#bouncePad(Player, org.bukkit.block.BlockFace)}
+ * for how the resulting block face is processed.
+ *
+ * @param event The event which gets called when a player moves.
+ */
+ @EventHandler
+ public void onPlayerMove(final PlayerMoveEvent event)
+ {
+ final Player player = event.getPlayer();
+ if (!this.hasPad(player))
+ {
+ return;
+ }
+
+ final BouncyPad pad = this.getPad(player);
+ final Location location = player.getLocation();
+
+ final Block xNeg1 = getRelative(location, -1, 0, 0);
+ final Block xPos1 = getRelative(location, 1, 0, 0);
+ final Block zNeg1 = getRelative(location, 0, 0, -1);
+ final Block zPos1 = getRelative(location, 0, 0, 1);
+ final Block yNeg1 = getRelative(location, 0, -1, 0);
+
+ Stream.of(xNeg1, xPos1, zNeg1, zPos1, yNeg1)
+ .filter(this::isWool)
+ .map(block -> block.getFace(location.getBlock()))
+ .findFirst()
+ .ifPresent(face -> pad.bouncePad(player, face));
+ }
+
+ /**
+ * Gets the relative block at the given location.
+ *
+ * @param location The location to get the relative block from.
+ * @param x The x mod.
+ * @param y The y mod.
+ * @param z The z mod.
+ * @return The relative block.
+ */
+ private Block getRelative(final Location location, final int x, final int y, final int z)
+ {
+ return location.getBlock().getRelative(x, y, z);
+ }
+
+ /**
+ * Checks if the given block is wool.
+ *
+ * @param block The block to check.
+ * @return True if the block is wool, false otherwise.
+ * @see Tag#WOOL
+ */
+ private boolean isWool(final Block block)
+ {
+ return Tag.WOOL.isTagged(block.getType());
+ }
+}
diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadType.java b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadType.java
new file mode 100644
index 0000000..1cc0f66
--- /dev/null
+++ b/Fossil/src/main/java/me/totalfreedom/fossil/bouncypads/PadType.java
@@ -0,0 +1,32 @@
+package me.totalfreedom.fossil.bouncypads;
+
+import org.bukkit.block.BlockFace;
+
+/**
+ * Represents a specific type of bouncy pad.
+ */
+public enum PadType
+{
+ /**
+ * A normal pad, which will only bounce the player if the face is {@link BlockFace#UP}.
+ */
+ NORMAL,
+ /**
+ * A pad which will bounce the player on {@link BlockFace#NORTH}, {@link BlockFace#SOUTH}, {@link BlockFace#EAST}
+ * or {@link BlockFace#WEST}.
+ */
+ SIDES,
+ /**
+ * A pad which will bounce the player if the face is {@link BlockFace#UP}, {@link BlockFace#NORTH},
+ * {@link BlockFace#EAST}, {@link BlockFace#SOUTH} or {@link BlockFace#WEST}.
+ */
+ ALL,
+ /**
+ * A pad which will bounce the player with an extreme velocity
+ */
+ EXTREME,
+ /**
+ * A pad which will send the player to space.
+ */
+ SPACE_CADET;
+}
diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/reactions/CopyCatReaction.java b/Fossil/src/main/java/me/totalfreedom/fossil/reactions/CopyCatReaction.java
new file mode 100644
index 0000000..983e5b4
--- /dev/null
+++ b/Fossil/src/main/java/me/totalfreedom/fossil/reactions/CopyCatReaction.java
@@ -0,0 +1,58 @@
+package me.totalfreedom.fossil.reactions;
+
+import me.totalfreedom.display.BossBarDisplay;
+import me.totalfreedom.economy.EconomicEntity;
+import me.totalfreedom.shop.Reaction;
+import me.totalfreedom.shop.ReactionType;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.bossbar.BossBar;
+
+import java.util.SplittableRandom;
+import java.util.function.Consumer;
+
+/**
+ * Represents a single chat reaction that can be performed by a player.
+ */
+public final class CopyCatReaction extends Reaction
+{
+ private final long reward;
+
+ public CopyCatReaction(final long reward)
+ {
+ super(ReactionType.COPYCAT);
+ this.reward = reward;
+ }
+
+ @Override
+ public long getReward()
+ {
+ return reward;
+ }
+
+ @Override
+ public void onReact(final Consumer entity)
+ {
+ entity.accept(null);
+ }
+
+ @Override
+ public void display(final Audience audience)
+ {
+ final BossBar bossBar = BossBarDisplay.builder().setName(getRandomCharacterString())
+ .setProgress(0.0F)
+ .build();
+ }
+
+ public String getRandomCharacterString() {
+ final SplittableRandom random = new SplittableRandom();
+ final StringBuilder sb = new StringBuilder(10);
+
+ final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ for (int i = 0; i < 10; i++) {
+ sb.append(chars.charAt(random.nextInt(chars.length())));
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/Patchwork/src/main/java/me/totalfreedom/api/Context.java b/Patchwork/src/main/java/me/totalfreedom/api/Context.java
index e4e7731..321cca6 100644
--- a/Patchwork/src/main/java/me/totalfreedom/api/Context.java
+++ b/Patchwork/src/main/java/me/totalfreedom/api/Context.java
@@ -1,5 +1,6 @@
package me.totalfreedom.api;
+import me.totalfreedom.provider.ContextProvider;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.World;
@@ -13,16 +14,40 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
+/**
+ * Represents an object context. This class is a simple generic type wrapper that can be used to ensure data types. This
+ * class is also used to provide a simple way to map data types.
+ *
+ * @param The type of the context.
+ * @see ContextProvider
+ */
@FunctionalInterface
public interface Context
{
+ /**
+ * Maps the context to another context.
+ *
+ * @param mapper The mapper function.
+ * @param The type of the mapped context.
+ * @return The mapped context.
+ */
default Context map(@NotNull final Function mapper)
{
return () -> mapper.apply(get());
}
+ /**
+ * Gets the context.
+ *
+ * @return The context.
+ */
T get();
+ /**
+ * Gets the context as a string.
+ *
+ * @return The context as a string.
+ */
default @Nullable String asString()
{
if (get() instanceof String string)
@@ -34,6 +59,11 @@ public interface Context
}
}
+ /**
+ * Gets the context as a boolean.
+ *
+ * @return The context as a boolean.
+ */
default @Nullable Boolean asBoolean()
{
if (get() instanceof Boolean bool)
@@ -45,6 +75,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Double}.
+ */
default @Nullable Double asDouble()
{
if (get() instanceof Double doub)
@@ -56,6 +89,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Integer}.
+ */
default @Nullable Integer asInt()
{
if (get() instanceof Integer integer)
@@ -67,6 +103,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Byte}.
+ */
default @Nullable Long asLong()
{
if (get() instanceof Long longg)
@@ -78,6 +117,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Float}.
+ */
default @Nullable Float asFloat()
{
if (get() instanceof Float floatt)
@@ -89,6 +131,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Player}.
+ */
default @Nullable Player asPlayer()
{
if (get() instanceof Player player)
@@ -100,6 +145,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link CommandSender}.
+ */
default @Nullable CommandSender asCommandSender()
{
if (get() instanceof CommandSender commandSender)
@@ -111,11 +159,19 @@ public interface Context
}
}
+ /**
+ * This is the same as calling {@link #get()} and then calling {@link Object#toString()} on the result.
+ *
+ * @return The context as a {@link String} literal.
+ */
default @NotNull String literal()
{
return get().toString();
}
+ /**
+ * @return The context as a {@link World}.
+ */
default @Nullable World asWorld()
{
if (get() instanceof World world)
@@ -127,6 +183,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Location}.
+ */
default @Nullable Location asLocation()
{
if (get() instanceof Location location)
@@ -138,6 +197,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link LivingEntity}.
+ */
default @Nullable LivingEntity asLivingEntity()
{
if (get() instanceof LivingEntity livingEntity)
@@ -149,6 +211,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Component}.
+ */
default @Nullable Component asComponent()
{
if (get() instanceof Component component)
@@ -160,6 +225,9 @@ public interface Context
}
}
+ /**
+ * @return The context as a {@link Projectile}.
+ */
default @Nullable Projectile asProjectile()
{
if (get() instanceof Projectile projectile)
@@ -171,6 +239,9 @@ public interface Context
}
}
+ /**
+ * @return The context as an {@link Action}.
+ */
default @Nullable Action asAction()
{
if (get() instanceof Action action)
@@ -182,6 +253,22 @@ public interface Context
}
}
+ /**
+ * Gets the context as a custom class. This will cast the object to the class if it is an instance of it.
+ *
+ * Typically, Context objects are useful when used to collect unknown data and then cast it to a known type.
+ *
+ * In the case where we know what the data should be but the compiler or the runtime does not, the object is wrapped
+ * in a Context which then exposes multiple methods to get the data as one of the known types.
+ *
+ * For example, if we have a Context<Object> and we already know that the wrapped object should be of type X,
+ * we can use X.class
on this method to retrieve the actual object. That is, to say, if there is not
+ * already a predefined method to get the object as the type we want.
+ *
+ * @param clazz
+ * @param
+ * @return
+ */
default @Nullable U asCustom(Class clazz)
{
if (clazz.isInstance(get()))
diff --git a/Patchwork/src/main/java/me/totalfreedom/api/Interpolator.java b/Patchwork/src/main/java/me/totalfreedom/api/Interpolator.java
index 7e71edc..0a6c598 100644
--- a/Patchwork/src/main/java/me/totalfreedom/api/Interpolator.java
+++ b/Patchwork/src/main/java/me/totalfreedom/api/Interpolator.java
@@ -1,7 +1,21 @@
package me.totalfreedom.api;
+/**
+ * Interpolates a range of values and returns the results in a {@link Double} array.
+ *
+ * This is a functional interface, to allow for lambda expressions, but also for anonymous custom interpolation
+ * implementations.
+ */
@FunctionalInterface
public interface Interpolator
{
+ /**
+ * Interpolates a range of values and returns the results in a {@link Double} array.
+ *
+ * @param from The starting value.
+ * @param to The ending value.
+ * @param max The number of values to interpolate.
+ * @return The interpolated values.
+ */
double[] interpolate(final double from, final double to, final int max);
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/api/Serializable.java b/Patchwork/src/main/java/me/totalfreedom/api/Serializable.java
index 499841b..41ab562 100644
--- a/Patchwork/src/main/java/me/totalfreedom/api/Serializable.java
+++ b/Patchwork/src/main/java/me/totalfreedom/api/Serializable.java
@@ -1,5 +1,12 @@
package me.totalfreedom.api;
+/**
+ * This interface represents a Serializable object.
+ * Objects which require custom serialization and cannot simply override or call
+ * the default {@link Object#toString()} method should implement this interface.
+ *
+ * @param The type of object to serialize
+ */
public interface Serializable
{
/**
@@ -11,5 +18,11 @@ public interface Serializable
*/
String serialize(T object);
- T deserialize(Context>... contexts);
+ /**
+ * Deserialize an object from a Serialized string..
+ * @param serializedObject The serialized object
+ * @return The deserialized object
+ */
+
+ T deserialize(String serializedObject);
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/audience/MutableAudienceForwarder.java b/Patchwork/src/main/java/me/totalfreedom/audience/MutableAudienceForwarder.java
index 4048c49..f725d73 100644
--- a/Patchwork/src/main/java/me/totalfreedom/audience/MutableAudienceForwarder.java
+++ b/Patchwork/src/main/java/me/totalfreedom/audience/MutableAudienceForwarder.java
@@ -19,16 +19,25 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
- * A replacement for {@link net.kyori.adventure.audience.ForwardingAudience} that allows for audiences to be removed
- * & added at will. Not thread safe.
+ * A replacement for {@link net.kyori.adventure.audience.ForwardingAudience} that allows for audiences to be removed &
+ * added at will. Not thread safe.
*
* This is intended for use in toggleable logging systems, for example, potion spy.
*/
// TODO: Work on thread-safety (or thread-safe alternative)
public class MutableAudienceForwarder implements Audience
{
+ /**
+ * The audiences that this forwards to.
+ */
private final Set audiences = new HashSet<>();
+ /**
+ * Creates a new {@link MutableAudienceForwarder} with the given audiences.
+ *
+ * @param audiences The audiences to forward to.
+ * @return The new {@link MutableAudienceForwarder}.
+ */
public static MutableAudienceForwarder from(final Audience... audiences)
{
final MutableAudienceForwarder audienceForwarder = new MutableAudienceForwarder();
@@ -41,6 +50,11 @@ public class MutableAudienceForwarder implements Audience
return audienceForwarder;
}
+ /**
+ * Adds an audience to this forwarder.
+ *
+ * @param audience The audience to add.
+ */
public void addAudience(final Audience audience)
{
if (audiences.contains(audience) || audience == this /* Protect against honest self-referential calls */)
@@ -51,12 +65,23 @@ public class MutableAudienceForwarder implements Audience
audiences.add(audience);
}
+ /**
+ * Removes an audience from this forwarder.
+ *
+ * @param audience The audience to remove.
+ * @return Whether the audience was removed.
+ */
public boolean removeAudience(final Audience audience)
{
return audiences.remove(audience);
}
-
+ /**
+ * Filters the audiences in the stream by the given predicate.
+ *
+ * @param filter a filter that determines if an audience should be included
+ * @return The first Audience found that matches the filter.
+ */
@Override
public @NotNull Audience filterAudience(@NotNull final Predicate super Audience> filter)
{
@@ -66,52 +91,103 @@ public class MutableAudienceForwarder implements Audience
.orElseThrow();
}
+ /**
+ * Applies a consumer to each audience in the stream.
+ *
+ * @param action the action to apply.
+ */
@Override
public void forEachAudience(@NotNull final Consumer super Audience> action)
{
audiences.forEach(action);
}
+ /**
+ * Sends a {@link ComponentLike} to every audience within the stream.
+ *
+ * @param message The message to send.
+ * @see Audience#sendMessage(ComponentLike)
+ * @see #forEachAudience(Consumer)
+ */
@Override
public void sendMessage(@NotNull final ComponentLike message)
{
- audiences.forEach(a -> a.sendMessage(message));
+ forEachAudience(a -> a.sendMessage(message));
}
+ /**
+ * Sends a {@link Component} to every audience within the stream.
+ *
+ * @param message The message to send
+ * @see Audience#sendMessage(Component)
+ * @see #forEachAudience(Consumer)
+ */
@Override
public void sendMessage(@NotNull final Component message)
{
- audiences.forEach(a -> a.sendMessage(message));
+ forEachAudience(a -> a.sendMessage(message));
}
+ /**
+ * Sends a {@link SignedMessage} to every audience within the stream.
+ *
+ * @param message the component content of the message
+ * @param boundChatType the bound chat type of the message
+ * @see Audience#sendMessage(Component, ChatType.Bound)
+ * @see #forEachAudience(Consumer)
+ */
@Override
public void sendMessage(@NotNull final Component message, final ChatType.@NotNull Bound boundChatType)
{
- audiences.forEach(a -> a.sendMessage(message, boundChatType));
+ forEachAudience(a -> a.sendMessage(message, boundChatType));
}
+ /**
+ * Sends a {@link SignedMessage} to every audience within the stream.
+ *
+ * @param message the component content of the message
+ * @param boundChatType the bound chat type of the message
+ * @see Audience#sendMessage(ComponentLike, ChatType.Bound)
+ * @see #forEachAudience(Consumer)
+ */
@Override
public void sendMessage(@NotNull final ComponentLike message, final ChatType.@NotNull Bound boundChatType)
{
- audiences.forEach(a -> a.sendMessage(message, boundChatType));
+ forEachAudience(a -> a.sendMessage(message, boundChatType));
}
+ /**
+ * Sends a {@link SignedMessage} to every audience within the stream.
+ *
+ * @param signedMessage the signed message data to send
+ * @param boundChatType the bound chat type of the message
+ */
@Override
public void sendMessage(@NotNull final SignedMessage signedMessage, final ChatType.@NotNull Bound boundChatType)
{
- audiences.forEach(a -> a.sendMessage(signedMessage, boundChatType));
+ forEachAudience(a -> a.sendMessage(signedMessage, boundChatType));
}
+ /**
+ * Deletes a signed message from the audiences chat.
+ *
+ * @param signedMessage the message to delete
+ */
@Override
public void deleteMessage(@NotNull final SignedMessage signedMessage)
{
- audiences.forEach(a -> a.deleteMessage(signedMessage));
+ forEachAudience(a -> a.deleteMessage(signedMessage));
}
+ /**
+ * Deletes a signed message from the audiences chat using the provided chat signature.
+ *
+ * @param signature the signature associated with the message to delete.
+ */
@Override
public void deleteMessage(final SignedMessage.@NotNull Signature signature)
{
- audiences.forEach(a -> a.deleteMessage(signature));
+ forEachAudience(a -> a.deleteMessage(signature));
}
// The methods below here will (probably) never be used, however it's good to keep them for completeness' sake.
@@ -119,127 +195,127 @@ public class MutableAudienceForwarder implements Audience
@Override
public void sendActionBar(@NotNull final ComponentLike message)
{
- audiences.forEach(a -> a.sendActionBar(message));
+ forEachAudience(a -> a.sendActionBar(message));
}
@Override
public void sendActionBar(@NotNull final Component message)
{
- audiences.forEach(a -> a.sendActionBar(message));
+ forEachAudience(a -> a.sendActionBar(message));
}
@Override
public void sendPlayerListHeader(@NotNull final ComponentLike header)
{
- audiences.forEach(a -> a.sendPlayerListHeader(header));
+ forEachAudience(a -> a.sendPlayerListHeader(header));
}
@Override
public void sendPlayerListHeader(@NotNull final Component header)
{
- audiences.forEach(a -> a.sendPlayerListHeader(header));
+ forEachAudience(a -> a.sendPlayerListHeader(header));
}
@Override
public void sendPlayerListFooter(@NotNull final ComponentLike footer)
{
- audiences.forEach(a -> a.sendPlayerListFooter(footer));
+ forEachAudience(a -> a.sendPlayerListFooter(footer));
}
@Override
public void sendPlayerListFooter(@NotNull final Component footer)
{
- audiences.forEach(a -> a.sendPlayerListFooter(footer));
+ forEachAudience(a -> a.sendPlayerListFooter(footer));
}
@Override
public void sendPlayerListHeaderAndFooter(@NotNull final ComponentLike header, @NotNull final ComponentLike footer)
{
- audiences.forEach(a -> a.sendPlayerListHeaderAndFooter(header, footer));
+ forEachAudience(a -> a.sendPlayerListHeaderAndFooter(header, footer));
}
@Override
public void sendPlayerListHeaderAndFooter(@NotNull final Component header, @NotNull final Component footer)
{
- audiences.forEach(a -> a.sendPlayerListHeaderAndFooter(header, footer));
+ forEachAudience(a -> a.sendPlayerListHeaderAndFooter(header, footer));
}
@Override
public void showTitle(@NotNull final Title title)
{
- audiences.forEach(a -> a.showTitle(title));
+ forEachAudience(a -> a.showTitle(title));
}
@Override
public void sendTitlePart(@NotNull final TitlePart part, @NotNull final T value)
{
- audiences.forEach(a -> a.sendTitlePart(part, value));
+ forEachAudience(a -> a.sendTitlePart(part, value));
}
@Override
public void clearTitle()
{
- audiences.forEach(Audience::clearTitle);
+ forEachAudience(Audience::clearTitle);
}
@Override
public void resetTitle()
{
- audiences.forEach(Audience::resetTitle);
+ forEachAudience(Audience::resetTitle);
}
@Override
public void showBossBar(@NotNull final BossBar bar)
{
- audiences.forEach(a -> a.showBossBar(bar));
+ forEachAudience(a -> a.showBossBar(bar));
}
@Override
public void hideBossBar(@NotNull final BossBar bar)
{
- audiences.forEach(a -> a.hideBossBar(bar));
+ forEachAudience(a -> a.hideBossBar(bar));
}
@Override
public void playSound(@NotNull final Sound sound)
{
- audiences.forEach(a -> a.playSound(sound));
+ forEachAudience(a -> a.playSound(sound));
}
@Override
public void playSound(@NotNull final Sound sound, final double x, final double y, final double z)
{
- audiences.forEach(a -> a.playSound(sound, x, y, z));
+ forEachAudience(a -> a.playSound(sound, x, y, z));
}
@Override
public void playSound(@NotNull final Sound sound, final Sound.@NotNull Emitter emitter)
{
- audiences.forEach(a -> a.playSound(sound, emitter));
+ forEachAudience(a -> a.playSound(sound, emitter));
}
@Override
public void stopSound(@NotNull final Sound sound)
{
- audiences.forEach(a -> a.stopSound(sound));
+ forEachAudience(a -> a.stopSound(sound));
}
@Override
public void stopSound(@NotNull final SoundStop stop)
{
- audiences.forEach(a -> a.stopSound(stop));
+ forEachAudience(a -> a.stopSound(stop));
}
@Override
public void openBook(final Book.@NotNull Builder book)
{
- audiences.forEach(a -> a.openBook(book));
+ forEachAudience(a -> a.openBook(book));
}
@Override
public void openBook(@NotNull final Book book)
{
- audiences.forEach(a -> a.openBook(book));
+ forEachAudience(a -> a.openBook(book));
}
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/base/CommonsBase.java b/Patchwork/src/main/java/me/totalfreedom/base/CommonsBase.java
index b75d402..911086e 100644
--- a/Patchwork/src/main/java/me/totalfreedom/base/CommonsBase.java
+++ b/Patchwork/src/main/java/me/totalfreedom/base/CommonsBase.java
@@ -6,12 +6,30 @@ import me.totalfreedom.service.SubscriptionProvider;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
+/**
+ * The base class for Patchwork.
+ */
public class CommonsBase extends JavaPlugin
{
+ /**
+ * The {@link EventBus} for this plugin.
+ */
private final EventBus eventBus = new EventBus(this);
+ /**
+ * The {@link Registration} object for this plugin.
+ */
private final Registration registration = new Registration();
+ /**
+ * The {@link FreedomExecutor} for this plugin.
+ */
private final FreedomExecutor executor = new FreedomExecutor();
+ /**
+ * Provides this plugin instance through a safe static method.
+ * This is effectively the same thing as using {@link JavaPlugin#getPlugin(Class)}
+ *
+ * @return the plugin instance
+ */
public static CommonsBase getInstance()
{
return JavaPlugin.getPlugin(CommonsBase.class);
@@ -22,34 +40,51 @@ public class CommonsBase extends JavaPlugin
{
Bukkit.getScheduler()
.runTaskLater(this, () -> getRegistrations()
- .getServiceRegistry()
- .stopAllServices(), 1L);
+ .getServiceTaskRegistry()
+ .stopAllServices(), 1L);
- getRegistrations().getServiceRegistry()
+ getRegistrations().getServiceTaskRegistry()
.unregisterService(EventBus.class);
}
@Override
public void onEnable()
{
- getRegistrations().getServiceRegistry()
+ getRegistrations().getServiceTaskRegistry()
.registerService(SubscriptionProvider.asyncService(this, eventBus));
getExecutor().getSync()
.execute(() -> getRegistrations()
- .getServiceRegistry()
- .startAllServices());
+ .getServiceTaskRegistry()
+ .startAllServices());
}
+ /**
+ * Gets the {@link FreedomExecutor} for this plugin.
+ *
+ * @return the {@link FreedomExecutor}
+ */
public FreedomExecutor getExecutor()
{
return executor;
}
+ /**
+ * Get's the Registration object for this plugin. This object contains every registry class for the various features
+ * provided by this plugin.
+ *
+ * @return the Registration object
+ */
public Registration getRegistrations()
{
return registration;
}
+ /**
+ * Gets the {@link EventBus} for this plugin. The EventBus is used to register and listen to custom events provided
+ * by Freedom Network Suite.
+ *
+ * @return the {@link EventBus}
+ */
public EventBus getEventBus()
{
return eventBus;
diff --git a/Patchwork/src/main/java/me/totalfreedom/base/Registration.java b/Patchwork/src/main/java/me/totalfreedom/base/Registration.java
index e23609a..1f5cf6c 100644
--- a/Patchwork/src/main/java/me/totalfreedom/base/Registration.java
+++ b/Patchwork/src/main/java/me/totalfreedom/base/Registration.java
@@ -7,16 +7,44 @@ import me.totalfreedom.data.ModuleRegistry;
import me.totalfreedom.data.ServiceTaskRegistry;
import me.totalfreedom.data.UserRegistry;
+/**
+ * This class is a holder for each registry in the data package.
+ *
+ * Registries such as {@link ModuleRegistry} and {@link ServiceTaskRegistry}
+ * can be found as final objects in this class. These registries should only ever be accessed through
+ * the single Registration object in CommonsBase using {@link CommonsBase#getRegistrations()}
+ */
public class Registration
{
+ /**
+ * The {@link EventRegistry}
+ */
private final EventRegistry eventRegistry;
+ /**
+ * The {@link UserRegistry}
+ */
private final UserRegistry userRegistry;
+ /**
+ * The {@link ServiceTaskRegistry}
+ */
private final ServiceTaskRegistry serviceTaskRegistry;
+ /**
+ * The {@link ModuleRegistry}
+ */
private final ModuleRegistry moduleRegistry;
+ /**
+ * The {@link GroupRegistry}
+ */
private final GroupRegistry groupRegistry;
+ /**
+ * The {@link ConfigRegistry}
+ */
private final ConfigRegistry configRegistry;
- public Registration()
+ /**
+ * Constructs a new Registration object and initializes all registries.
+ */
+ Registration()
{
this.eventRegistry = new EventRegistry();
this.userRegistry = new UserRegistry();
@@ -26,31 +54,49 @@ public class Registration
this.configRegistry = new ConfigRegistry();
}
+ /**
+ * @return The {@link ModuleRegistry}
+ */
public ModuleRegistry getModuleRegistry()
{
return moduleRegistry;
}
+ /**
+ * @return The {@link EventRegistry}
+ */
public EventRegistry getEventRegistry()
{
return eventRegistry;
}
+ /**
+ * @return The {@link UserRegistry}
+ */
public UserRegistry getUserRegistry()
{
return userRegistry;
}
- public ServiceTaskRegistry getServiceRegistry()
+ /**
+ * @return The {@link ServiceTaskRegistry}
+ */
+ public ServiceTaskRegistry getServiceTaskRegistry()
{
return serviceTaskRegistry;
}
+ /**
+ * @return The {@link GroupRegistry}
+ */
public GroupRegistry getGroupRegistry()
{
return groupRegistry;
}
+ /**
+ * @return The {@link ConfigRegistry}
+ */
public ConfigRegistry getConfigRegistry()
{
return configRegistry;
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegate.java b/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegate.java
index a888edb..b6d6276 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegate.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegate.java
@@ -22,7 +22,23 @@ import java.util.Arrays;
import java.util.List;
import java.util.Set;
-public class BukkitDelegate extends Command implements PluginIdentifiableCommand
+/**
+ * This class is acts as a delegate between our custom command implementation and the Bukkit API.
+ *
+ * This class is not meant to be used directly, and is only public to allow for the Bukkit API to access it.
+ * As a result, this file will remain undocumented.
+ *
+ *
+ * This class is not thread-safe.
+ *
+ * This class is not meant to be extended.
+ *
+ * This class is not meant to be instantiated.
+ *
+ * This class is not meant to be used outside Patchwork.
+ *
+ */
+public final class BukkitDelegate extends Command implements PluginIdentifiableCommand
{
private final JavaPlugin plugin;
private final Commander command;
@@ -50,8 +66,8 @@ public class BukkitDelegate extends Command implements PluginIdentifiableCommand
@Override
public boolean execute(@NotNull final CommandSender sender,
- @NotNull final String commandLabel,
- @NotNull final String[] args)
+ @NotNull final String commandLabel,
+ @NotNull final String[] args)
{
if (sender instanceof ConsoleCommandSender && noConsole)
{
@@ -87,8 +103,7 @@ public class BukkitDelegate extends Command implements PluginIdentifiableCommand
{
command.getBaseMethod()
.invoke(command, sender);
- }
- catch (Exception ex)
+ } catch (Exception ex)
{
FreedomLogger.getLogger("Patchwork")
.error(ex);
@@ -101,8 +116,8 @@ public class BukkitDelegate extends Command implements PluginIdentifiableCommand
}
private void processSubCommands(final @NotNull String @NotNull [] args,
- final CommandSender sender, final ContextProvider provider,
- final Subcommand node)
+ final CommandSender sender, final ContextProvider provider,
+ final Subcommand node)
{
final Class>[] argTypes = node.args();
if (argTypes.length != args.length)
@@ -126,8 +141,7 @@ public class BukkitDelegate extends Command implements PluginIdentifiableCommand
command.getSubcommands()
.get(node)
.invoke(command, sender, objects);
- }
- catch (Exception ex)
+ } catch (Exception ex)
{
FreedomLogger.getLogger("Patchwork")
.error(ex);
@@ -159,16 +173,16 @@ public class BukkitDelegate extends Command implements PluginIdentifiableCommand
.map(World::getName)
.toList());
case "%number%" -> results.addAll(List.of(
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9"));
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9"));
case "%location%" -> results.add("world,x,y,z");
default -> results.add(p);
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/CommandHandler.java b/Patchwork/src/main/java/me/totalfreedom/command/CommandHandler.java
index c2714bf..34c754d 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/CommandHandler.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/CommandHandler.java
@@ -1,17 +1,39 @@
package me.totalfreedom.command;
import org.bukkit.Bukkit;
+import org.bukkit.command.CommandMap;
import org.bukkit.plugin.java.JavaPlugin;
+/**
+ * Handles the registration of commands.
+ * The plugin which initializes this class should be the plugin that is registering the commands.
+ */
public class CommandHandler
{
+ /**
+ * The plugin that this command handler is registered to.
+ *
+ * This should be the plugin instance which is trying to register the commands.
+ */
private final JavaPlugin plugin;
+ /**
+ * Creates a new command handler.
+ *
+ * @param plugin The plugin that this command handler is registered to.
+ */
public CommandHandler(final JavaPlugin plugin)
{
this.plugin = plugin;
}
+ /**
+ * Registers a command. This method will automatically delegate the command information to the Bukkit API and
+ * register with the {@link CommandMap}.
+ *
+ * @param command The command to register.
+ * @param The type of the command.
+ */
public void registerCommand(final T command)
{
final BukkitDelegate delegate = new BukkitDelegate(command);
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/Commander.java b/Patchwork/src/main/java/me/totalfreedom/command/Commander.java
index 62f6d56..2830a84 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/Commander.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/Commander.java
@@ -17,15 +17,60 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
+/**
+ * This is the base command class which should be extended when creating a new command. Commands must be annotated with
+ * the {@link Info} and {@link Permissive} annotations in order to be properly registered with the
+ * {@link CommandHandler}.
+ *
+ * One single method can be annotated with the {@link Base} annotation to specify that method should be called when the
+ * command is executed without any arguments.
+ *
+ * You are allowed to have as many methods as you want which are annotated with the {@link Subcommand} annotation. These
+ * methods will be called when the command is executed with the specified subcommand.
+ *
+ * You are also allowed to use multiple {@link Completion} annotations per method to define multiple tab completions for
+ * a single subcommand. This would be useful in the case where you would like to include specific completion cases, but
+ * also support basic String completion cases.
+ *
+ * When creating {@link Completion} annotations, you only need to register arguments a single time per class. For more
+ * information, see {@link Subcommand}.
+ */
public abstract class Commander
{
+ /**
+ * The plugin which owns this command.
+ */
private final JavaPlugin plugin;
+ /**
+ * The {@link Info} annotation for this command.
+ */
private final Info info;
+ /**
+ * The {@link Permissive} annotation for this command.
+ */
private final Permissive perms;
+ /**
+ * A map of all subcommands and their related methods for this command.
+ */
private final Map subcommands;
+ /**
+ * A set of all {@link Completion} annotations for this command.
+ */
private final Set completions;
+ /**
+ * The method which should be called when the command is executed without any arguments.
+ */
private final Method baseMethod;
+ /**
+ * Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
+ * command.
+ *
+ * This constructor will automatically register all subcommands and completions for this command. It will also
+ * automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
+ *
+ * @param plugin The plugin which contains this command.
+ */
protected Commander(final @NotNull JavaPlugin plugin)
{
this.info = this.getClass()
@@ -44,7 +89,7 @@ public abstract class Commander
.filter(m -> m.isAnnotationPresent(Base.class))
.findFirst()
.orElseThrow(() -> new RuntimeException(
- "Base annotation present but no method found."));
+ "Base annotation present but no method found."));
this.baseMethod = method;
} else
@@ -55,14 +100,17 @@ public abstract class Commander
registerAnnotations();
}
+ /**
+ * Registers all subcommands and completions for this command.
+ */
private void registerAnnotations()
{
Stream.of(this.getClass()
.getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(Subcommand.class))
.forEach(method -> this.subcommands.put(
- method.getDeclaredAnnotation(Subcommand.class),
- method));
+ method.getDeclaredAnnotation(Subcommand.class),
+ method));
List.of(this.getClass()
.getDeclaredAnnotationsByType(Completion.class))
@@ -70,36 +118,66 @@ public abstract class Commander
.forEach(completions::add);
}
+ /**
+ * Gets the method which should be called when the command is executed without any arguments.
+ *
+ * This method will return null if the command does not have a base method.
+ *
+ * @return The base method for this command, or null if the command does not have a base method.
+ */
@Nullable
public Method getBaseMethod()
{
return baseMethod;
}
+ /**
+ * Gets the {@link Info} annotation for this command.
+ *
+ * This method will never return null as this annotation is required for the command to be registered.
+ *
+ * @return The {@link Info} annotation for this command.
+ */
@NotNull
Info getInfo()
{
return this.info;
}
+ /**
+ * Gets the {@link Permissive} annotation for this command.
+ *
+ * This method will never return null as this annotation is required for the command to be registered.
+ *
+ * @return The {@link Permissive} annotation for this command.
+ */
@NotNull
Permissive getPerms()
{
return this.perms;
}
+ /**
+ * @return The plugin which owns this command.
+ */
@NotNull
public JavaPlugin getPlugin()
{
return this.plugin;
}
+ /**
+ * @return A map of all subcommands and their related methods for this command.
+ */
@NotNull
Map getSubcommands()
{
return this.subcommands;
}
+ /**
+ * @return A set of all {@link Completion} annotations for this command.
+ */
@Nullable
Set getCompletions()
{
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java
index 253fc86..d32efa4 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java
@@ -6,12 +6,26 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * Represents a tab completion for a command.
+ *
+ * This will register at class level, and does not retain method information. As a result, you only need to register the
+ * arguments a single time, and it will always be used in tab completions.
+ */
@Target(ElementType.METHOD)
@Repeatable(Completions.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Completion
{
+ /**
+ * An array of possible arguments for this particular index, represented by {@link #index()}.
+ *
+ * @return An array of possible arguments for tab completion.
+ */
String[] args();
+ /**
+ * @return The index in which these arguments should be shown.
+ */
int index();
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completions.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completions.java
index 632156d..694804e 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completions.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completions.java
@@ -5,9 +5,18 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * A marker interface which represents a holder for multiple {@link Completion} annotations.
+ *
+ * This interface is NOT intended for implementation and should
+ * NOT be used.
+ */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Completions
{
+ /**
+ * @return The {@link Completion} annotations.
+ */
Completion[] value();
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Info.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Info.java
index 478a5ad..fd90d82 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Info.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Info.java
@@ -3,14 +3,40 @@ package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * This interface holds the information for each command. This annotation defines the command's name, description,
+ * usage, and aliases. Commands must have this annotation present to be registered with the handler.
+ */
@Retention(RetentionPolicy.RUNTIME)
public @interface Info
{
+ /**
+ * Technically, this is the only required value you must supply yourself.
+ * However, it is HIGHLY recommended you supply the other optional values as well,
+ * for better customization of your command.
+ *
+ * @return The command's name.
+ */
String name();
+ /**
+ * By default, this is set to "This is the default command description."
+ *
+ * @return The command's description.
+ */
String description() default "This is the default command description.";
+ /**
+ * By default, this is set to "/<command>"
+ *
+ * @return The command's usage.
+ */
String usage() default "/";
+ /**
+ * By default, this returns an empty array.
+ *
+ * @return The command's aliases.
+ */
String[] aliases() default {};
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Permissive.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Permissive.java
index 470569d..90c5739 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Permissive.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Permissive.java
@@ -3,12 +3,31 @@ package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * This annotation holds the permission information for each command. This annotation defines the command's permission,
+ * whether it is only for players, and the message to send if the sender does not have permission to use the command.
+ *
+ * Classes MUST have this annotation present to be registered with the handler.
+ */
@Retention(RetentionPolicy.RUNTIME)
public @interface Permissive
{
+ /**
+ * @return The command's permission.
+ */
String perm();
+ /**
+ * By default, this is set to false.
+ *
+ * @return True if the command is only for players, false otherwise.
+ */
boolean onlyPlayers() default false;
+ /**
+ * By default, this is set to "You do not have permission to use this command."
+ *
+ * @return The message to send if the sender does not have permission to use the command.
+ */
String noPerms() default "You do not have permission to use this command.";
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java
index 3010844..9a3b8c7 100644
--- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java
+++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java
@@ -1,15 +1,41 @@
package me.totalfreedom.command.annotation;
+import me.totalfreedom.command.CommandHandler;
+import me.totalfreedom.provider.ContextProvider;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * This annotation should be used to mark methods as subcommand methods.
+ * Subcommand methods can have custom arguments (current supported arguments can be found in the {@link ContextProvider}),
+ * and can also have a custom permission. These subcommands can also be annotated with {@link Completions} to provide
+ * tab completions for the subcommand. The subcommand method must be public, and must be in a class that is registered
+ * with the {@link CommandHandler}.
+ *
+ * Tab completions with the {@link Completions} annotation are only supported for subcommands. When registering
+ * completions, you only need to define the completion arguments a single time. If there are other methods which
+ * function as optional additional arguments for the subcommand, the previously registered arguments will still be
+ * present when the user does their tab completion.
+ *
+ * For example, if you have a subcommand method with the arguments {@code (Player, String)}, and another method which
+ * has the arguments {@code (Player, String, String)}, the tab completions for the second method will still have the
+ * {@code Player} and {@code String} arguments registered from the first method. You will only need to provide a
+ * {@link Completion} for the additional 3rd argument.
+ */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subcommand
{
+ /**
+ * @return The permission to use when executing this subcommand.
+ */
String permission();
+ /**
+ * @return The arguments, as classes, to use when registering this subcommand.
+ */
Class>[] args() default {};
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/config/Configuration.java b/Patchwork/src/main/java/me/totalfreedom/config/Configuration.java
index 6b551c1..728ead2 100644
--- a/Patchwork/src/main/java/me/totalfreedom/config/Configuration.java
+++ b/Patchwork/src/main/java/me/totalfreedom/config/Configuration.java
@@ -1,36 +1,132 @@
package me.totalfreedom.config;
+import me.totalfreedom.api.Context;
+import me.totalfreedom.provider.ContextProvider;
+
import java.io.File;
import java.io.IOException;
import java.util.List;
+/**
+ * Represents a configuration file of any type.
+ */
public interface Configuration
{
+ /**
+ * Saves the configuration to the file.
+ *
+ * @throws IOException If the operation cannot be completed.
+ */
void save() throws IOException;
+ /**
+ * Loads the configuration from the file.
+ *
+ * @throws IOException If the operation cannot be completed.
+ */
void load() throws IOException;
+ /**
+ * @return The name of the file.
+ */
String getFileName();
+ /**
+ * @return The actual Configuration {@link File}.
+ */
File getConfigurationFile();
+ /**
+ * Gets a String object from the associated path.
+ *
+ * @param path The path to get the String from.
+ * @return The String object.
+ */
String getString(String path);
+ /**
+ * Gets a Boolean object from the associated path.
+ *
+ * @param path The path to get the Boolean from.
+ * @return The Boolean object.
+ */
Boolean getBoolean(String path);
+ /**
+ * Gets a List object from the associated path. This method will use {@link Context}s and the
+ * {@link ContextProvider} to get the object types in the list. If the objects cannot be inferred, the method will
+ * return a list of generic {@link Object}s.
+ *
+ * @param path The path to get the List from.
+ * @param The type of the objects in the list.
+ * @return The List object.
+ */
List getList(String path);
+ /**
+ * Gets a List object from the associated path. The List that is returned will be the String values which are stored
+ * within the configuration file at the given path.
+ *
+ * @param path The path to get the List from.
+ * @return The List object.
+ */
List getStringList(String path);
+ /**
+ * Gets an Integer from the associated path.
+ *
+ * @param path The path to get the Integer from.
+ * @return The Integer object.
+ */
Integer getInt(String path);
+ /**
+ * Gets a Long from the associated path.
+ *
+ * @param path The path to get the Long from.
+ * @return The Long object.
+ */
Long getLong(String path);
+ /**
+ * Gets a Double from the associated path.
+ *
+ * @param path The path to get the Double from.
+ * @return The Double object.
+ */
Double getDouble(String path);
+ /**
+ * Sets the value at the given path to the given value.
+ *
+ * @param path The path to set the value at.
+ * @param value The value to set.
+ * @param The type of the value.
+ */
void set(String path, T value);
- T get(String path, Class type);
+ /**
+ * Gets the value at the given path as the given type.
+ *
+ * This method will use {@link Context}s and the {@link ContextProvider} to get the object type. If the object type
+ * cannot be inferred, the method will return a generic {@link Object}.
+ *
+ * @param path The path to get the value from.
+ * @param The type of the value.
+ * @return The value at the given path.
+ */
+ T get(String path);
- T getOrDefault(String path, Class type, T fallback);
+ /**
+ * Gets the value at the given path as the given type.
+ *
+ * This method will use {@link Context}s and the {@link ContextProvider} to get the object type. If the object type
+ * cannot be inferred, the method will return the given fallback value.
+ *
+ * @param path The path to get the value from.
+ * @param fallback The fallback value to return if the value at the given path is null.
+ * @param The type of the value.
+ * @return The value at the given path.
+ */
+ T getOrDefault(String path, T fallback);
}
\ No newline at end of file
diff --git a/Patchwork/src/main/java/me/totalfreedom/data/ConfigRegistry.java b/Patchwork/src/main/java/me/totalfreedom/data/ConfigRegistry.java
index 2c54b02..8f6b9e9 100644
--- a/Patchwork/src/main/java/me/totalfreedom/data/ConfigRegistry.java
+++ b/Patchwork/src/main/java/me/totalfreedom/data/ConfigRegistry.java
@@ -5,20 +5,40 @@ import me.totalfreedom.config.Configuration;
import java.util.HashMap;
import java.util.Map;
+/**
+ * A registry for all the configurations.
+ */
public class ConfigRegistry
{
+ /**
+ * A map of all the configurations.
+ */
private final Map configurationList = new HashMap<>();
+ /**
+ * Registers a configuration.
+ * @param name The name of the configuration.
+ * @param configuration The configuration.
+ */
public void register(final String name, final Configuration configuration)
{
configurationList.put(name, configuration);
}
+ /**
+ * Unregisters a configuration.
+ * @param name The name of the configuration.
+ */
public void unregister(final String name)
{
configurationList.remove(name);
}
+ /**
+ * Gets a configuration.
+ * @param name The name of the configuration.
+ * @return The configuration.
+ */
public Configuration getConfiguration(final String name)
{
return configurationList.get(name);
diff --git a/Patchwork/src/main/java/me/totalfreedom/data/EventRegistry.java b/Patchwork/src/main/java/me/totalfreedom/data/EventRegistry.java
index b98d6cb..99f7271 100644
--- a/Patchwork/src/main/java/me/totalfreedom/data/EventRegistry.java
+++ b/Patchwork/src/main/java/me/totalfreedom/data/EventRegistry.java
@@ -6,25 +6,49 @@ import me.totalfreedom.provider.EventProvider;
import java.util.ArrayList;
import java.util.List;
+/**
+ * A registry for {@link FEvent}s.
+ */
public class EventRegistry
{
+ /**
+ * The list of events.
+ */
private final List events;
+ /**
+ * Creates a new event registry.
+ */
public EventRegistry()
{
this.events = new ArrayList<>();
}
+ /**
+ * Registers an event.
+ * @param event The event to register.
+ */
public void register(final FEvent event)
{
this.events.add(event);
}
+ /**
+ * Unregisters an event.
+ * @param event The event to unregister.
+ */
public void unregister(final FEvent event)
{
this.events.remove(event);
}
+ /**
+ * Gets an {@link EventProvider} for the specified event class which contains the actual {@link FEvent} instance.
+ *
+ * @param clazz The event class.
+ * @return The event provider.
+ * @param The event type.
+ */
public EventProvider getEvent(final Class clazz)
{
for (final FEvent event : this.events)
diff --git a/Patchwork/src/main/java/me/totalfreedom/data/GroupRegistry.java b/Patchwork/src/main/java/me/totalfreedom/data/GroupRegistry.java
index 12e6c57..96ae921 100644
--- a/Patchwork/src/main/java/me/totalfreedom/data/GroupRegistry.java
+++ b/Patchwork/src/main/java/me/totalfreedom/data/GroupRegistry.java
@@ -6,25 +6,49 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import java.util.ArrayList;
import java.util.List;
+/**
+ * A registry for {@link Group}s.
+ */
public class GroupRegistry
{
+ /**
+ * The list of groups.
+ */
private final List groups;
+ /**
+ * Creates a new group registry.
+ */
public GroupRegistry()
{
this.groups = new ArrayList<>();
}
+ /**
+ * Registers a group.
+ * @param group The group to register.
+ * @return {@code true} if the group was registered, {@code false} otherwise.
+ */
public boolean registerGroup(final Group group)
{
return groups.add(group);
}
+ /**
+ * Unregisters a group.
+ * @param group The group to unregister.
+ * @return {@code true} if the group was unregistered, {@code false} otherwise.
+ */
public boolean unregisterGroup(final Group group)
{
return groups.remove(group);
}
+ /**
+ * Gets a group by name.
+ * @param name The name of the group.
+ * @return The group, or {@code null} if no group was found.
+ */
public Group getGroup(final String name)
{
final PlainTextComponentSerializer s = PlainTextComponentSerializer.plainText();
@@ -39,6 +63,9 @@ public class GroupRegistry
return null;
}
+ /**
+ * @return The list of groups.
+ */
public List getGroups()
{
return groups;
diff --git a/Patchwork/src/main/java/me/totalfreedom/data/ModuleRegistry.java b/Patchwork/src/main/java/me/totalfreedom/data/ModuleRegistry.java
index 34f024a..cdc26c0 100644
--- a/Patchwork/src/main/java/me/totalfreedom/data/ModuleRegistry.java
+++ b/Patchwork/src/main/java/me/totalfreedom/data/ModuleRegistry.java
@@ -6,15 +6,29 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.util.ArrayList;
import java.util.List;
+/**
+ * A registry for modules.
+ */
public class ModuleRegistry
{
+ /**
+ * The list of modules.
+ */
private final List plugins;
+ /**
+ * Creates a new module registry.
+ */
public ModuleRegistry()
{
this.plugins = new ArrayList<>();
}
+ /**
+ * Adds a module to the registry.
+ *
+ * @param plugin The module to add.
+ */
public void addModule(final JavaPlugin plugin)
{
if (this.plugins.contains(plugin))
@@ -24,19 +38,30 @@ public class ModuleRegistry
this.plugins.add(plugin);
}
+ /**
+ * Removes a module from the registry.
+ *
+ * @param plugin The module to remove.
+ */
public void removeModule(final JavaPlugin plugin)
{
this.plugins.remove(plugin);
}
- @SuppressWarnings("unchecked")
- public ModuleProvider getModule(final Class clazz)
+ /**
+ * Gets a module from the registry wrapped in a {@link ModuleProvider}.
+ *
+ * @param clazz The class of the module.
+ * @param The type of the module.
+ * @return The module.
+ */
+ public ModuleProvider getProvider(final Class clazz)
{
for (final JavaPlugin plugin : plugins)
{
if (clazz.isInstance(plugin))
{
- return () -> (T) plugin;
+ return () -> clazz.cast(plugin);
}
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/data/UserRegistry.java b/Patchwork/src/main/java/me/totalfreedom/data/UserRegistry.java
index b5f47e4..184e140 100644
--- a/Patchwork/src/main/java/me/totalfreedom/data/UserRegistry.java
+++ b/Patchwork/src/main/java/me/totalfreedom/data/UserRegistry.java
@@ -7,39 +7,91 @@ import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
+/**
+ * A registry for {@link UserData} objects.
+ */
public class UserRegistry
{
+ /**
+ * A map of {@link User} objects to {@link UserData} objects.
+ */
private final Map userDataMap;
+ /**
+ * Creates a new {@link UserRegistry}.
+ */
public UserRegistry()
{
this.userDataMap = new HashMap<>();
}
+ /**
+ * Gets the {@link UserData} object for the given {@link User}.
+ *
+ * @param user The {@link User} to get the {@link UserData} for.
+ * @return The {@link UserData} object for the given {@link User}.
+ */
public UserData getUserData(final User user)
{
return userDataMap.get(user);
}
- public UserData fromPlayer(final Player player) {
+ /**
+ * Gets the {@link UserData} object for the given {@link Player}.
+ */
+ public UserData fromPlayer(final Player player)
+ {
return userDataMap.entrySet()
.stream()
- .filter(entry -> entry.getKey().getUniqueId().equals(player.getUniqueId()))
+ .filter(entry -> entry.getKey()
+ .getUniqueId()
+ .equals(player.getUniqueId()))
.findFirst()
.map(Map.Entry::getValue)
.orElse(null);
}
+ /**
+ * Gets the {@link User} object for the given {@link Player}.
+ *
+ * @param player The {@link Player} to get the {@link User} for.
+ * @return The {@link User} object for the given {@link Player}.
+ */
+ public User getUser(final Player player)
+ {
+ return userDataMap.entrySet()
+ .stream()
+ .filter(entry -> entry.getKey()
+ .getUniqueId()
+ .equals(player.getUniqueId()))
+ .findFirst()
+ .map(Map.Entry::getKey)
+ .orElse(null);
+ }
+
+ /**
+ * Registers the given {@link User} and {@link UserData} objects.
+ * @param user The {@link User} to register.
+ * @param userData The {@link UserData} to register.
+ */
public void registerUserData(final User user, final UserData userData)
{
userDataMap.put(user, userData);
}
+ /**
+ * Unregisters the given {@link User} and {@link UserData} objects.
+ * @param user The {@link User} to unregister.
+ */
public void unregisterUserData(final User user)
{
userDataMap.remove(user);
}
+ /**
+ * Gets the map of {@link User} objects to {@link UserData} objects.
+ * @return The map of {@link User} objects to {@link UserData} objects.
+ */
public Map getUserDataMap()
{
return userDataMap;
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/AbstractMenu.java b/Patchwork/src/main/java/me/totalfreedom/display/AbstractMenu.java
index 697f7eb..9baa4df 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/AbstractMenu.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/AbstractMenu.java
@@ -15,14 +15,34 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+/**
+ * Represents a menu that can be opened by a player.
+ */
public abstract class AbstractMenu
{
+ /**
+ * A map of all menus by their UUID.
+ */
private static final Map invByUUID = new HashMap<>();
+ /**
+ * A map of all open menus by the player's UUID.
+ */
private static final Map openInvs = new HashMap<>();
private final Map actionMap;
+ /**
+ * The displayable that represents this menu.
+ */
private final Displayable displayable;
+ /**
+ * The UUID of the displayable that represents this menu.
+ */
private final UUID displayableUUID;
+ /**
+ * Creates a new menu with the specified size.
+ *
+ * @param size The size of the menu.
+ */
protected AbstractMenu(final int size)
{
actionMap = new HashMap<>();
@@ -32,26 +52,48 @@ public abstract class AbstractMenu
invByUUID.put(getDisplayableUUID(), this);
}
- public UUID getDisplayableUUID()
- {
- return displayableUUID;
- }
-
+ /**
+ * @return A map of all menus by their UUID.
+ */
public static Map getInvByUUID()
{
return invByUUID;
}
+ /**
+ * @return A map of all open menus by the player's UUID.
+ */
public static Map getOpenInvs()
{
return openInvs;
}
+ /**
+ * @return The displayable UUID of this menu.
+ */
+ public UUID getDisplayableUUID()
+ {
+ return displayableUUID;
+ }
+
+ /**
+ * Sets the item at the specified slot.
+ *
+ * @param slot The slot to set the item at.
+ * @param stack The item to set.
+ */
public void setItem(final int slot, final @NotNull ItemStack stack)
{
setItem(slot, stack, null);
}
+ /**
+ * Sets the item at the specified slot.
+ *
+ * @param slot The slot to set the item at.
+ * @param stack The item to set.
+ * @param action The action to perform when the item is clicked.
+ */
public void setItem(final int slot, final @NotNull ItemStack stack, final @Nullable ClickAction action)
{
getDisplayable().setItem(slot, stack);
@@ -61,17 +103,28 @@ public abstract class AbstractMenu
}
}
+ /**
+ * @return The displayable that represents this menu.
+ */
public Displayable getDisplayable()
{
return displayable;
}
+ /**
+ * Opens this menu for the specified player.
+ *
+ * @param player The player to open the menu for.
+ */
public void open(final @NotNull Player player)
{
player.openInventory(getDisplayable());
openInvs.put(player.getUniqueId(), getDisplayableUUID());
}
+ /**
+ * Deletes this menu.
+ */
public void delete()
{
Bukkit.getOnlinePlayers()
@@ -87,24 +140,47 @@ public abstract class AbstractMenu
invByUUID.remove(getDisplayableUUID());
}
+ /**
+ * Closes this menu for the specified player.
+ *
+ * @param player The player to close the menu for.
+ */
public void close(final @NotNull Player player)
{
player.closeInventory();
openInvs.remove(player.getUniqueId());
}
+ /**
+ * @return A map of all actions by their slot.
+ */
public Map getActions()
{
return actionMap;
}
+ /**
+ * Creates a new item with the specified material and name.
+ *
+ * @param material The material of the item.
+ * @param name The name of the item.
+ * @return The created item.
+ */
public ItemStack newItem(final @NotNull Material material, final @NotNull Component name)
{
return this.newItem(material, name, new Component[0]);
}
+ /**
+ * Creates a new item with the specified material, name, and lore.
+ *
+ * @param material The material of the item.
+ * @param name The name of the item.
+ * @param lore The lore of the item.
+ * @return The created item.
+ */
public ItemStack newItem(final @NotNull Material material, final @NotNull Component name,
- final @NotNull Component... lore)
+ final @NotNull Component... lore)
{
final ItemStack item = new ItemStack(material, 1);
final ItemMeta meta = item.getItemMeta();
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/BossBarDisplay.java b/Patchwork/src/main/java/me/totalfreedom/display/BossBarDisplay.java
index 3c59ccd..6ef8948 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/BossBarDisplay.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/BossBarDisplay.java
@@ -11,65 +11,126 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+/**
+ * This class is a wrapper for {@link BossBar} objects. It provides some handy methods for changing the boss bar's
+ * properties, displaying the bar to {@link Audience}s, and a {@link BossBarBuilder} to easily create new boss bars.
+ */
public class BossBarDisplay
{
private BossBar bossBar;
+ /**
+ * Creates a new {@link BossBarDisplay} object.
+ *
+ * @param bossBar The {@link BossBar} to wrap.
+ */
public BossBarDisplay(final BossBar bossBar)
{
this.bossBar = bossBar;
}
+ /**
+ * @return A new {@link BossBarBuilder} object.
+ */
public static BossBarBuilder builder()
{
return new BossBarBuilder();
}
+ /**
+ * Changes the boss bar's color.
+ *
+ * @param color The new color.
+ */
public void changeColor(final BossBar.Color color)
{
this.bossBar.color(color);
}
+ /**
+ * Changes the boss bar's color.
+ *
+ * @param overlay The new overlay.
+ */
public void changeOverlay(final BossBar.Overlay overlay)
{
this.bossBar.overlay(overlay);
}
+ /**
+ * Changes the boss bar's name using a {@link Component}.
+ *
+ * @param name The new name.
+ */
public void changeName(final Component name)
{
this.bossBar.name(name);
}
+ /**
+ * Changes the boss bar's name with a String and a {@link TextColor}.
+ *
+ * @param name The new name.
+ * @param color The name color.
+ */
public void changeName(final String name, final TextColor color)
{
this.bossBar.name(Component.text(name, color));
}
+ /**
+ * Changes the boss bar's name with a String.
+ *
+ * @param name The new name.
+ */
public void changeName(final String name)
{
this.bossBar.name(Component.text(name));
}
+ /**
+ * Shows this Boss Bar to the specified {@link Audience}.
+ *
+ * @param audience The {@link Audience} to show the Boss Bar to.
+ */
public void showTo(final Audience audience)
{
audience.showBossBar(getBossBar());
}
+ /**
+ * @return The {@link BossBar} object that this class wraps.
+ */
public BossBar getBossBar()
{
return this.bossBar;
}
+ /**
+ * Sets the {@link BossBar} object that this class wraps.
+ *
+ * @param bossBar The new {@link BossBar} object.
+ */
public void setBossBar(final BossBar bossBar)
{
this.bossBar = bossBar;
}
+ /**
+ * Hides this Boss Bar from the specified {@link Audience}.
+ *
+ * @param audience The {@link Audience} to hide the Boss Bar from.
+ */
public void hideFrom(final Audience audience)
{
audience.hideBossBar(getBossBar());
}
+ /**
+ * Increments the Bar's progress by the specified amount. This must be a range from 0 to 100.
+ *
+ * @param progress The new progress.
+ */
public void incrementProgress(final @Range(from = 0, to = 100) float progress)
{
final float currentProgress = this.bossBar.progress();
@@ -79,6 +140,11 @@ public class BossBarDisplay
else this.bossBar.progress(newProgress);
}
+ /**
+ * Decrements the Bar's progress by the specified amount. This must be a range from 0 to 100.
+ *
+ * @param progress The new progress.
+ */
public void decrementProgress(final @Range(from = 0, to = 100) float progress)
{
final float currentProgress = this.bossBar.progress();
@@ -88,40 +154,80 @@ public class BossBarDisplay
else this.bossBar.progress(newProgress);
}
+ /**
+ * Sets the Bar's progress to the maximum amount (full bar).
+ */
public void maximumProgress()
{
this.bossBar.progress(1.0F);
}
+ /**
+ * Sets the Bar's progress to half of the maximum amount (half bar).
+ */
public void halfProgress()
{
this.bossBar.progress(0.5F);
}
+ /**
+ * Sets the Bar's progress to the minimum amount (empty bar).
+ */
public void minimumProgress()
{
this.bossBar.progress(0.0F);
}
+ /**
+ * Shows this Boss Bar to the specified {@link ForwardingAudience}.
+ *
+ * @param forwardingAudience The {@link ForwardingAudience} to show the Boss Bar to.
+ */
public void showForwarded(final ForwardingAudience forwardingAudience)
{
forwardingAudience.showBossBar(getBossBar());
}
+ /**
+ * Hides this Boss Bar from the specified {@link ForwardingAudience}.
+ *
+ * @param forwardingAudience The {@link ForwardingAudience} to hide the Boss Bar from.
+ */
public void hideForwarded(final ForwardingAudience forwardingAudience)
{
forwardingAudience.hideBossBar(getBossBar());
}
- private static final class BossBarBuilder
+ /**
+ * A Builder class for {@link BossBar} objects.
+ */
+ public static final class BossBarBuilder
{
+ /**
+ * The flags that this Boss Bar will have.
+ */
private final Set flags = new HashSet<>();
+ /**
+ * The Boss Bar's name.
+ */
private Component name;
+ /**
+ * The Boss Bar's color.
+ */
private BossBar.Color color;
+ /**
+ * The Boss Bar's overlay.
+ */
private BossBar.Overlay overlay;
+ /**
+ * The Boss Bar's progress.
+ */
@Range(from = 0, to = 1)
private float progress;
+ /**
+ * Initializes this Builder object.
+ */
public BossBarBuilder()
{
this.name = Component.empty();
@@ -130,72 +236,134 @@ public class BossBarDisplay
this.progress = 0.0F;
}
+ /**
+ * Sets the name of the boss bar.
+ *
+ * @param name The name of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setName(final Component name)
{
this.name = name;
return this;
}
+ /**
+ * Sets the name of the boss bar using a String and a {@link TextColor}.
+ *
+ * @param name The name of the boss bar.
+ * @param color The color of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setName(final String name, final TextColor color)
{
this.name = Component.text(name, color);
return this;
}
+ /**
+ * Sets the name of the boss bar using a String.
+ *
+ * @param name The name of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setName(final String name)
{
this.name = Component.text(name);
return this;
}
+ /**
+ * Adds a flag to the boss bar.
+ * @param flag The flag to add.
+ * @return The builder.
+ */
public BossBarBuilder addFlag(final BossBar.Flag flag)
{
this.flags.add(flag);
return this;
}
+ /**
+ * Adds multiple flags to the boss bar.
+ * @param flags The flags to add.
+ * @return The builder.
+ */
public BossBarBuilder addFlags(final BossBar.Flag... flags)
{
this.flags.addAll(List.of(flags));
return this;
}
+ /**
+ * Removes a flag from the boss bar.
+ * @param flag The flag to remove.
+ * @return The builder.
+ */
public BossBarBuilder removeFlag(final BossBar.Flag flag)
{
this.flags.remove(flag);
return this;
}
+ /**
+ * Removes multiple flags from the boss bar.
+ * @param flags The flags to remove.
+ * @return The builder.
+ */
public BossBarBuilder removeFlags(final BossBar.Flag... flags)
{
this.flags.removeAll(List.of(flags));
return this;
}
+ /**
+ * Clears all flags from the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder clearFlags()
{
this.flags.clear();
return this;
}
+ /**
+ * Sets the color of the boss bar.
+ * @param color The color of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setColor(final BossBar.Color color)
{
this.color = color;
return this;
}
+ /**
+ * Sets the overlay of the boss bar.
+ * @param overlay The overlay of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setOverlay(final BossBar.Overlay overlay)
{
this.overlay = overlay;
return this;
}
+ /**
+ * Sets the progress of the boss bar. This must satisfy {@code 0 <= progress <= 100}.
+ * @param progress The progress of the boss bar.
+ * @return The builder.
+ */
public BossBarBuilder setProgress(final @Range(from = 0, to = 100) float progress)
{
this.progress = progress / 100.0F;
return this;
}
+ /**
+ * Builds the boss bar.
+ * @return The {@link BossBar}.
+ */
public BossBar build()
{
return BossBar.bossBar(this.name, this.progress, this.color, this.overlay, this.flags);
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/BossBarTimer.java b/Patchwork/src/main/java/me/totalfreedom/display/BossBarTimer.java
new file mode 100644
index 0000000..91cf858
--- /dev/null
+++ b/Patchwork/src/main/java/me/totalfreedom/display/BossBarTimer.java
@@ -0,0 +1,40 @@
+package me.totalfreedom.display;
+
+import me.totalfreedom.service.Task;
+import net.kyori.adventure.audience.Audience;
+import org.bukkit.Bukkit;
+
+import java.time.Duration;
+
+public class BossBarTimer extends Task
+{
+ private final BossBarDisplay bossBarDisplay;
+ private final Duration duration;
+ private double seconds = 0;
+
+ public BossBarTimer(final BossBarDisplay bossBarDisplay, final Duration duration)
+ {
+ super("BossBarTimer", -1L, 20L);
+ this.bossBarDisplay = bossBarDisplay;
+ this.duration = duration;
+ bossBarDisplay.minimumProgress();
+ bossBarDisplay.showTo(Bukkit.getServer());
+ }
+
+ @Override
+ public void run()
+ {
+ if (this.isCancelled()) return;
+
+ if (seconds >= duration.getSeconds())
+ {
+ bossBarDisplay.hideFrom(Bukkit.getServer());
+ this.cancel();
+ return;
+ }
+
+ final float percentage = (float) (seconds / duration.getSeconds()) * 100L;
+ bossBarDisplay.incrementProgress(percentage);
+ seconds++;
+ }
+}
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/ClickAction.java b/Patchwork/src/main/java/me/totalfreedom/display/ClickAction.java
index 2d6d21f..a7816c7 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/ClickAction.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/ClickAction.java
@@ -2,8 +2,15 @@ package me.totalfreedom.display;
import org.bukkit.entity.Player;
+/**
+ * Represents an action to be performed when a player clicks on an inventory slot in the respective {@link AbstractMenu}.
+ */
@FunctionalInterface
public interface ClickAction
{
+ /**
+ * Called when a player clicks on an inventory slot in the respective {@link AbstractMenu}.
+ * @param player The player who clicked.
+ */
void onClick(final Player player);
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/Displayable.java b/Patchwork/src/main/java/me/totalfreedom/display/Displayable.java
index c550877..56a5fff 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/Displayable.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/Displayable.java
@@ -15,12 +15,29 @@ import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
+/**
+ * A class that represents an inventory that can be displayed to players. This class also represents the inventory
+ * holder which contains the inventory.
+ */
public final class Displayable implements Inventory, InventoryHolder
{
+ /**
+ * The size of the inventory. This is always a multiple of 9.
+ */
private final int size;
+ /**
+ * The contents of the inventory.
+ */
private ItemStack[] contents;
+ /**
+ * Creates a new Displayable inventory with the given size. You are free to supply any size you want, but it will
+ * always be rounded up to the next multiple of 9. The maximum size allowed is 54. Any number higher than that will
+ * be rounded down to 54.
+ *
+ * @param size The size of the inventory.
+ */
protected Displayable(final int size)
{
if (size < 1 || size > 54)
@@ -31,8 +48,8 @@ public final class Displayable implements Inventory, InventoryHolder
// If the size is not a multiple of nine, find the difference to the next highest multiple of 9 and make up
// the difference.
this.size = (size % 9 == 0)
- ? size
- : size + (9 - size % 9);
+ ? size
+ : size + (9 - size % 9);
this.contents = new ItemStack[size];
}
@@ -82,7 +99,7 @@ public final class Displayable implements Inventory, InventoryHolder
@Override
public @NotNull HashMap addItem(final @NotNull ItemStack... items)
- throws IllegalArgumentException
+ throws IllegalArgumentException
{
final HashMap remainingItems = new HashMap<>();
@@ -121,7 +138,7 @@ public final class Displayable implements Inventory, InventoryHolder
@Override
public @NotNull HashMap removeItem(final @NotNull ItemStack... items)
- throws IllegalArgumentException
+ throws IllegalArgumentException
{
final HashMap removedItems = new HashMap<>();
@@ -154,7 +171,7 @@ public final class Displayable implements Inventory, InventoryHolder
if (remainingAmount < item.getAmount())
{
removedItems.put(removedItems.size(),
- new ItemStack(item.getType(), item.getAmount() - remainingAmount));
+ new ItemStack(item.getType(), item.getAmount() - remainingAmount));
}
}
@@ -163,7 +180,7 @@ public final class Displayable implements Inventory, InventoryHolder
@Override
public @NotNull HashMap removeItemAnySlot(final @NotNull ItemStack... items)
- throws IllegalArgumentException
+ throws IllegalArgumentException
{
return removeItem(items);
}
@@ -294,7 +311,7 @@ public final class Displayable implements Inventory, InventoryHolder
@Override
public @NotNull HashMap all(final @NotNull Material material)
- throws IllegalArgumentException
+ throws IllegalArgumentException
{
final HashMap matchingItems = new HashMap<>();
for (int i = 0; i < size; i++)
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/DisplayableView.java b/Patchwork/src/main/java/me/totalfreedom/display/DisplayableView.java
index 9a5eb2a..2a923b1 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/DisplayableView.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/DisplayableView.java
@@ -8,14 +8,42 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
+/**
+ * A view of a {@link Displayable} inventory.
+ *
+ * This class can be used to display two separate {@link Displayable} objects to the Player.
+ */
public class DisplayableView extends InventoryView
{
+ /**
+ * The upper inventory involved in this transaction.
+ */
private final Displayable top;
+ /**
+ * The lower inventory involved in this transaction.
+ */
private final Displayable bottom;
+ /**
+ * The player viewing the inventories involved in this transaction.
+ */
private final Player player;
+ /**
+ * The type of inventory this transaction is for.
+ */
private final InventoryType type;
+ /**
+ * The title of the main inventory involved in this transaction. The main inventory should always be the top
+ * inventory.
+ */
private String title;
+ /**
+ * Creates a new DisplayableView.
+ *
+ * @param player The player viewing the inventories involved in this transaction.
+ * @param top The upper inventory involved in this transaction.
+ * @param bottom The lower inventory involved in this transaction.
+ */
public DisplayableView(final Player player, final Displayable top, final Displayable bottom)
{
this.player = player;
@@ -55,15 +83,15 @@ public class DisplayableView extends InventoryView
return title;
}
- @Override
- public @NotNull String getOriginalTitle()
- {
- return FreedomAdventure.toPlainText(type.defaultTitle());
- }
-
@Override
public void setTitle(final @NotNull String title)
{
this.title = title;
}
+
+ @Override
+ public @NotNull String getOriginalTitle()
+ {
+ return FreedomAdventure.toPlainText(type.defaultTitle());
+ }
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/display/TitleDisplay.java b/Patchwork/src/main/java/me/totalfreedom/display/TitleDisplay.java
index 1a7a4e4..a5110bd 100644
--- a/Patchwork/src/main/java/me/totalfreedom/display/TitleDisplay.java
+++ b/Patchwork/src/main/java/me/totalfreedom/display/TitleDisplay.java
@@ -8,50 +8,108 @@ import net.kyori.adventure.title.Title;
import java.time.Duration;
+/**
+ * A wrapper class for {@link Title}s that allows for easy display to an {@link Audience}.
+ */
public class TitleDisplay
{
+ /**
+ * The {@link Title} to display.
+ */
private Title title;
+ /**
+ * Creates a new {@link TitleDisplay} with the given {@link Title}.
+ * @param title The {@link Title} to display.
+ */
public TitleDisplay(final Title title)
{
this.title = title;
}
+ /**
+ * @return A new {@link TitleBuilder} which can be used to create new {@link Title}s.
+ */
public static TitleBuilder builder()
{
return new TitleBuilder();
}
+ /**
+ * Displays the {@link Title} to the given {@link Audience}.
+ * @param audience The {@link Audience} to display the {@link Title} to.
+ */
public void displayTo(final Audience audience)
{
audience.clearTitle();
audience.showTitle(getTitle());
}
+ /**
+ * @return The {@link Title} to display.
+ */
public Title getTitle()
{
return this.title;
}
+ /**
+ * Sets the {@link Title} to display.
+ * @param title The {@link Title} to display.
+ */
public void setTitle(final Title title)
{
this.title = title;
}
+ /**
+ * Displays the {@link Title} to the given {@link ForwardingAudience}.
+ * @param forwardingAudience The {@link ForwardingAudience} to display the {@link Title} to.
+ */
public void displayForwarded(final ForwardingAudience forwardingAudience)
{
forwardingAudience.clearTitle();
forwardingAudience.showTitle(getTitle());
}
- private static final class TitleBuilder
+ /**
+ * A builder class for {@link Title}s.
+ */
+ public static final class TitleBuilder
{
+ /**
+ * The main title of the {@link Title}.
+ */
private Component mainTitle;
+ /**
+ * The subtitle of the {@link Title}.
+ */
private Component subTitle;
+ /**
+ * How long the Title should fade in for.
+ */
private Duration fadeIn;
+ /**
+ * How long the Title should fade out for.
+ */
private Duration fadeOut;
+ /**
+ * How long the Title should be displayed for.
+ */
private Duration displayDuration;
+ /**
+ * Creates a new {@link TitleBuilder} with default values.
+ * The default values are:
+ *
+ * - Empty main title
+ * - Empty subtitle
+ * - Default fade in time
+ * - Default fade out time
+ * - Default display duration
+ *
+ * @see Title#DEFAULT_TIMES
+ */
public TitleBuilder()
{
this.mainTitle = Component.empty();
@@ -61,60 +119,111 @@ public class TitleDisplay
this.displayDuration = Title.DEFAULT_TIMES.stay();
}
+ /**
+ * Sets the main title of the {@link Title}.
+ * @param title The main title of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setMainTitle(final String title)
{
this.mainTitle = Component.text(title);
return this;
}
+ /**
+ * Sets the main title of the {@link Title}.
+ * @param title The main title of the {@link Title}.
+ * @param titleColor The color of the main title.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setMainTitle(final String title, final TextColor titleColor)
{
this.mainTitle = Component.text(title, titleColor);
return this;
}
+ /**
+ * Sets the main title of the {@link Title}.
+ * @param mainTitle The main title of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setMainTitle(final Component mainTitle)
{
this.mainTitle = mainTitle;
return this;
}
+ /**
+ * Sets the subtitle of the {@link Title}.
+ * @param title The subtitle of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setSubTitle(final String title)
{
this.subTitle = Component.text(title);
return this;
}
+ /**
+ * Sets the subtitle of the {@link Title}.
+ * @param title The subtitle of the {@link Title}.
+ * @param titleColor The color of the subtitle.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setSubTitle(final String title, final TextColor titleColor)
{
this.subTitle = Component.text(title, titleColor);
return this;
}
+ /**
+ * Sets the subtitle of the {@link Title}.
+ * @param subTitle The subtitle of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setSubTitle(final Component subTitle)
{
this.subTitle = subTitle;
return this;
}
+ /**
+ * Sets the fade in time of the {@link Title}.
+ * @param duration The fade in time of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setFadeIn(final Duration duration)
{
this.fadeIn = duration;
return this;
}
+ /**
+ * Sets the fade out time of the {@link Title}.
+ * @param duration The fade out time of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setFadeOut(final Duration duration)
{
this.fadeOut = duration;
return this;
}
+ /**
+ * Sets the display duration of the {@link Title}.
+ * @param duration The display duration of the {@link Title}.
+ * @return The {@link TitleBuilder} instance.
+ */
public TitleBuilder setDisplayDuration(final Duration duration)
{
this.displayDuration = duration;
return this;
}
+ /**
+ * Builds the {@link Title} with the given parameters.
+ * @return The built {@link Title}.
+ */
public Title build()
{
return Title.title(
diff --git a/Patchwork/src/main/java/me/totalfreedom/service/ServiceSubscription.java b/Patchwork/src/main/java/me/totalfreedom/service/ServiceSubscription.java
index f6c4a83..25bcb8e 100644
--- a/Patchwork/src/main/java/me/totalfreedom/service/ServiceSubscription.java
+++ b/Patchwork/src/main/java/me/totalfreedom/service/ServiceSubscription.java
@@ -16,7 +16,23 @@ public final class ServiceSubscription
private boolean isActive = false;
+ ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service)
+ {
+ this(plugin, service, 1L, false);
+ }
+
ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service, final boolean async)
+ {
+ this(plugin, service, 1L, async);
+ }
+
+ ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service, final long interval)
+ {
+ this(plugin, service, interval, false);
+ }
+
+ ServiceSubscription(@NotNull final JavaPlugin plugin, @NotNull final T service,
+ final long interval, final boolean async)
{
this.service = service;
this.async = async;
@@ -28,7 +44,7 @@ public final class ServiceSubscription
this.executor = r ->
{
final BukkitTask task = Bukkit.getScheduler()
- .runTaskTimerAsynchronously(plugin, r, 0, 1);
+ .runTaskTimerAsynchronously(plugin, r, 0, interval);
tempId[0] = task.getTaskId();
};
} else
@@ -36,7 +52,7 @@ public final class ServiceSubscription
this.executor = r ->
{
final BukkitTask task = Bukkit.getScheduler()
- .runTaskTimer(plugin, r, 0, 1);
+ .runTaskTimer(plugin, r, 0, interval);
tempId[0] = task.getTaskId();
};
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/service/SubscriptionProvider.java b/Patchwork/src/main/java/me/totalfreedom/service/SubscriptionProvider.java
index a1478c0..cd2e846 100644
--- a/Patchwork/src/main/java/me/totalfreedom/service/SubscriptionProvider.java
+++ b/Patchwork/src/main/java/me/totalfreedom/service/SubscriptionProvider.java
@@ -16,7 +16,16 @@ public final class SubscriptionProvider
public static final ServiceSubscription syncService(@NotNull final JavaPlugin plugin,
@NotNull final S service)
{
- return new ServiceSubscription<>(plugin, service, false);
+ return new ServiceSubscription<>(plugin, service);
+ }
+
+ @NotNull
+ @Contract(value = "_,_,_ -> new", pure = false)
+ public static final ServiceSubscription syncService(@NotNull final JavaPlugin plugin,
+ final long interval,
+ @NotNull final S service)
+ {
+ return new ServiceSubscription<>(plugin, service, interval);
}
@NotNull
@@ -35,6 +44,15 @@ public final class SubscriptionProvider
return new TaskSubscription<>(plugin, task, false);
}
+ @NotNull
+ @Contract(value = "_,_,_ -> new", pure = false)
+ public static final ServiceSubscription asyncService(@NotNull final JavaPlugin plugin,
+ final long interval,
+ @NotNull final S service)
+ {
+ return new ServiceSubscription<>(plugin, service, interval, true);
+ }
+
@NotNull
@Contract(value = "_, _ -> new", pure = false)
public static final TaskSubscription runAsyncTask(@NotNull final JavaPlugin plugin,
diff --git a/Patchwork/src/main/java/me/totalfreedom/service/Task.java b/Patchwork/src/main/java/me/totalfreedom/service/Task.java
index b072b26..88049e1 100644
--- a/Patchwork/src/main/java/me/totalfreedom/service/Task.java
+++ b/Patchwork/src/main/java/me/totalfreedom/service/Task.java
@@ -1,20 +1,85 @@
package me.totalfreedom.service;
-public interface Task extends Runnable
+import me.totalfreedom.utils.DurationTools;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.time.Duration;
+
+public abstract class Task extends BukkitRunnable
{
- boolean isRunning();
+ private final String name;
+ private long delay;
+ private long interval;
- String getName();
+ protected Task(final String name)
+ {
+ this(name, -1L, -1L);
+ }
- boolean isRepeating();
+ protected Task(final String name, final long delay, final long interval)
+ {
+ this.name = name;
+ this.delay = delay;
+ this.interval = interval;
+ }
- void setRepeating(long interval);
+ protected Task(final String name, final long delay)
+ {
+ this(name, delay, -1L);
+ }
- boolean isDelayed();
+ protected Task(final String name, final Duration delay)
+ {
+ this(name, DurationTools.getTickedSeconds(delay), -1L);
+ }
- void setDelayed(long delay);
+ protected Task(final String name, final Duration delay, final Duration interval)
+ {
+ this(name, DurationTools.getTickedSeconds(delay), DurationTools.getTickedSeconds(interval));
+ }
- long getInterval();
+ protected Task(final String name, final long delay, final Duration interval)
+ {
+ this(name, delay, DurationTools.getTickedSeconds(interval));
+ }
- long getDelay();
+ public boolean isRunning()
+ {
+ return !isCancelled();
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public boolean isRepeating()
+ {
+ return interval != -1L;
+ }
+
+ public void setRepeating(final long interval)
+ {
+ this.interval = interval;
+ }
+
+ public boolean isDelayed()
+ {
+ return this.delay != -1;
+ }
+
+ public void setDelayed(final long delay)
+ {
+ this.delay = delay;
+ }
+
+ public long getInterval()
+ {
+ return interval;
+ }
+
+ public long getDelay()
+ {
+ return delay;
+ }
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/shop/Reactable.java b/Patchwork/src/main/java/me/totalfreedom/shop/Reactable.java
index c8dacd2..6a00d84 100644
--- a/Patchwork/src/main/java/me/totalfreedom/shop/Reactable.java
+++ b/Patchwork/src/main/java/me/totalfreedom/shop/Reactable.java
@@ -1,16 +1,25 @@
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
{
- String getReactionMessage();
+ Component getReactionMessage();
Duration getReactionDuration();
ReactionType getReactionType();
+ long getReward();
+
+ void display(final Audience audience);
+
void onReact(final EconomicEntity entity);
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/shop/Reaction.java b/Patchwork/src/main/java/me/totalfreedom/shop/Reaction.java
new file mode 100644
index 0000000..6124f8b
--- /dev/null
+++ b/Patchwork/src/main/java/me/totalfreedom/shop/Reaction.java
@@ -0,0 +1,69 @@
+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;
+
+/**
+ * Represents a chat reaction that can be performed by a player.
+ */
+public abstract class Reaction implements Reactable
+{
+ private final Duration reactionDuration;
+ private final ReactionType reactionType;
+ private final long reward;
+ private Component reactionMessage = Component.empty();
+
+ protected Reaction(final ReactionType type)
+ {
+ this(50L, type);
+ }
+
+ protected Reaction(final long reward, final ReactionType type)
+ {
+ this(30L, reward, type);
+ }
+
+ protected Reaction(final long seconds, final long reward, final ReactionType reactionType)
+ {
+ this(Duration.ofSeconds(seconds), reward, reactionType);
+ }
+
+ protected Reaction(final Duration duration, final long reward, final ReactionType reactionType)
+ {
+ this.reward = reward;
+ this.reactionDuration = duration;
+ this.reactionType = reactionType;
+ }
+
+ @Override
+ public Component getReactionMessage()
+ {
+ return reactionMessage;
+ }
+
+ @Override
+ public Duration getReactionDuration()
+ {
+ return reactionDuration;
+ }
+
+ @Override
+ public ReactionType getReactionType()
+ {
+ return reactionType;
+ }
+
+ @Override
+ public BossBarDisplay getBossBarDisplay()
+ {
+
+ }
+
+ public void setReactionMessage(final Component message)
+ {
+ this.reactionMessage = message;
+ }
+}
diff --git a/Patchwork/src/main/java/me/totalfreedom/shop/ReactionTask.java b/Patchwork/src/main/java/me/totalfreedom/shop/ReactionTask.java
new file mode 100644
index 0000000..4af9233
--- /dev/null
+++ b/Patchwork/src/main/java/me/totalfreedom/shop/ReactionTask.java
@@ -0,0 +1,50 @@
+package me.totalfreedom.shop;
+
+import io.papermc.paper.event.player.AsyncChatEvent;
+import me.totalfreedom.base.CommonsBase;
+import me.totalfreedom.display.BossBarDisplay;
+import me.totalfreedom.display.BossBarTimer;
+import me.totalfreedom.economy.EconomicEntity;
+import me.totalfreedom.service.Task;
+import net.kyori.adventure.bossbar.BossBar;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class ReactionTask extends Task implements Listener {
+ private final Reaction reaction;
+ private final BossBarDisplay bossBarDisplay;
+
+ public ReactionTask(final String name, final Reaction reaction) {
+ super(name, -1L, -1);
+ this.reaction = reaction;
+ final BossBar bossBar = BossBarDisplay.builder()
+ .setName(reaction.getReactionMessage())
+ .setColor(BossBar.Color.GREEN)
+ .setProgress(0.0F)
+ .build();
+
+ this.bossBarDisplay = new BossBarDisplay(bossBar);
+ }
+
+ @Override
+ public void run() {
+ if (isCancelled()) {
+ }
+
+ final BossBarTimer timer = new BossBarTimer(bossBarDisplay, reaction.getReactionDuration());
+ timer.runTaskTimer(CommonsBase.getInstance(), 0L, timer.getInterval());
+ }
+
+ @EventHandler
+ public void onPlayerChat(final AsyncChatEvent event) {
+ if (event.message()
+ .equals(reaction.getReactionMessage())) {
+ final EconomicEntity entity = CommonsBase.getInstance()
+ .getRegistrations()
+ .getUserRegistry()
+ .getUser(event.getPlayer());
+
+ reaction.onReact(entity);
+ }
+ }
+}
diff --git a/Patchwork/src/main/java/me/totalfreedom/shop/ReactionType.java b/Patchwork/src/main/java/me/totalfreedom/shop/ReactionType.java
index b2b308d..e36a3da 100644
--- a/Patchwork/src/main/java/me/totalfreedom/shop/ReactionType.java
+++ b/Patchwork/src/main/java/me/totalfreedom/shop/ReactionType.java
@@ -2,5 +2,5 @@ package me.totalfreedom.shop;
public enum ReactionType
{
- COPYCAT, UNSCRAMBLE;
+ COPYCAT, UNSCRAMBLE, MATH;
}
diff --git a/Patchwork/src/main/java/me/totalfreedom/utils/DurationTools.java b/Patchwork/src/main/java/me/totalfreedom/utils/DurationTools.java
new file mode 100644
index 0000000..1f833bc
--- /dev/null
+++ b/Patchwork/src/main/java/me/totalfreedom/utils/DurationTools.java
@@ -0,0 +1,28 @@
+package me.totalfreedom.utils;
+
+import java.time.Duration;
+
+public final class DurationTools
+{
+ // One tick is 1/20th of a second which is about 50ms.
+ public static final Duration TICK = Duration.ofMillis(50L);
+ // One second is 20 ticks.
+ public static final Duration SECOND = TICK.multipliedBy(20L);
+ // One minute is 60 seconds.
+ public static final Duration MINUTE = SECOND.multipliedBy(60L);
+
+ private DurationTools()
+ {
+ throw new AssertionError();
+ }
+
+ public static final long getTickedSeconds(final Duration duration)
+ {
+ return duration.toMillis() / 50L;
+ }
+
+ public static final Duration getTickedSeconds(final long seconds)
+ {
+ return SECOND.multipliedBy(seconds);
+ }
+}