mirror of
https://github.com/SimplexDevelopment/FreedomNetworkSuite.git
synced 2024-11-14 05:03:33 +00:00
Merge pull request #3 from AtlasMediaGroup/Displayable-GUI-API
- Implement GUI API - Documentation for a majority of the API in Patchwork - Implement Particle API - Implement interpolation utils for color interpolation (blocks, particles, and text applicable) - Finish Command API - Remove Banning API - Created utility classes for various features of the Kyori Adventure API - Removed TFM
This commit is contained in:
commit
9ef703ae7b
@ -6,18 +6,20 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
public class Corvo extends JavaPlugin
|
||||
{
|
||||
@Override
|
||||
public void onEnable() {
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getModuleRegistry()
|
||||
.addModule(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
public void onDisable()
|
||||
{
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getModuleRegistry()
|
||||
.removeModule(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable()
|
||||
{
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getModuleRegistry()
|
||||
.addModule(this);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package me.totalfreedom.corvo.listener;
|
||||
|
||||
import io.papermc.paper.event.block.BlockBreakBlockEvent;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
|
||||
public class PlayerInteractionListener implements Listener
|
||||
{
|
||||
@EventHandler
|
||||
public void playerBreakBlock(final BlockBreakEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void blockBreakBlock(final BlockBreakBlockEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void playerOpenContainer(final InventoryOpenEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void playerCloseContainer(final InventoryCloseEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void playerInteraction(final PlayerInteractEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -5,6 +5,7 @@ import me.totalfreedom.datura.punishment.Cager;
|
||||
import me.totalfreedom.datura.punishment.Halter;
|
||||
import me.totalfreedom.datura.punishment.Locker;
|
||||
import me.totalfreedom.datura.sql.MySQL;
|
||||
import me.totalfreedom.service.SubscriptionProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@ -24,14 +25,36 @@ public class Datura extends JavaPlugin
|
||||
.getModuleRegistry()
|
||||
.addModule(this);
|
||||
|
||||
CommonsBase.getInstance().getRegistrations().getServiceRegistry().register(this, locker);
|
||||
CommonsBase.getInstance().getRegistrations().getServiceRegistry().register(this, cager);
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getServiceTaskRegistry()
|
||||
.registerService(SubscriptionProvider.syncService(this, locker));
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getServiceTaskRegistry()
|
||||
.registerService(SubscriptionProvider.syncService(this, cager));
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(halter, this);
|
||||
Bukkit.getPluginManager()
|
||||
.registerEvents(halter, this);
|
||||
}
|
||||
|
||||
public MySQL getSQL()
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
public Halter getHalter()
|
||||
{
|
||||
return halter;
|
||||
}
|
||||
|
||||
public Locker getLocker()
|
||||
{
|
||||
return locker;
|
||||
}
|
||||
|
||||
public Cager getCager()
|
||||
{
|
||||
return cager;
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
package me.totalfreedom.datura.banning;
|
||||
|
||||
import me.totalfreedom.security.ban.BanID;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoField;
|
||||
|
||||
public final class BanUID implements BanID
|
||||
{
|
||||
private final char prefix;
|
||||
private final int numericalTag;
|
||||
|
||||
private BanUID(final boolean permanent)
|
||||
{
|
||||
if (permanent)
|
||||
{
|
||||
prefix = 'P';
|
||||
} else
|
||||
{
|
||||
prefix = 'T';
|
||||
}
|
||||
|
||||
final Instant instant = Instant.now();
|
||||
|
||||
final String stringBuilder = String.valueOf(instant.get(ChronoField.DAY_OF_YEAR)) + // The first three numbers between 001 -> 365
|
||||
instant.get(ChronoField.HOUR_OF_DAY) + // next two numbers between 00 -> 23
|
||||
instant.get(ChronoField.MINUTE_OF_HOUR) + // next two numbers between 00 -> 59
|
||||
instant.get(ChronoField.MILLI_OF_SECOND); // last three numbers between 000 -> 999
|
||||
|
||||
numericalTag = Integer.parseInt(stringBuilder);
|
||||
}
|
||||
|
||||
public static BanUID createTempID()
|
||||
{
|
||||
return new BanUID(false);
|
||||
}
|
||||
|
||||
public static BanUID createPermID()
|
||||
{
|
||||
return new BanUID(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID()
|
||||
{
|
||||
return getIDPrefix() + "-" + getNumericalTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getIDPrefix()
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumericalTag()
|
||||
{
|
||||
return numericalTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermanent()
|
||||
{
|
||||
return getIDPrefix() == 'P';
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package me.totalfreedom.datura.banning;
|
||||
|
||||
import me.totalfreedom.security.ban.Ban;
|
||||
import me.totalfreedom.security.ban.BanID;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class SimpleBan implements Ban
|
||||
{
|
||||
private final BanID id;
|
||||
private final UUID offenderID;
|
||||
private final String reason;
|
||||
private final String issuer;
|
||||
private final Instant creationTime;
|
||||
private final Instant expiry;
|
||||
|
||||
public SimpleBan(
|
||||
final UUID offenderID,
|
||||
final String reason,
|
||||
final String issuer,
|
||||
final Instant creationTime,
|
||||
final Instant expiry)
|
||||
{
|
||||
if (expiry == null)
|
||||
{
|
||||
this.id = BanUID.createPermID();
|
||||
} else
|
||||
{
|
||||
this.id = BanUID.createTempID();
|
||||
}
|
||||
|
||||
this.offenderID = offenderID;
|
||||
this.reason = reason;
|
||||
this.issuer = issuer;
|
||||
this.creationTime = creationTime;
|
||||
this.expiry = expiry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BanID getBanID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getOffenderID()
|
||||
{
|
||||
return offenderID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReason()
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBanIssuer()
|
||||
{
|
||||
return issuer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime()
|
||||
{
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Instant getExpiry()
|
||||
{
|
||||
return expiry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired()
|
||||
{
|
||||
return Instant.now().compareTo(expiry) >= 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2023 TotalFreedom
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
* software and associated documentation files (the “Software”), to deal in the Software
|
||||
* without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.totalfreedom.datura.cmd;
|
||||
|
||||
import me.totalfreedom.command.Commander;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Info;
|
||||
import me.totalfreedom.command.annotation.Permissive;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import me.totalfreedom.datura.Datura;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Info(name = "cage", description = "Cage a player.",
|
||||
usage = "/cage <player> <on|off> [material]")
|
||||
@Permissive(perm = "datura.cage")
|
||||
public class CageCommand extends Commander
|
||||
{
|
||||
protected CageCommand(final @NotNull JavaPlugin plugin)
|
||||
{
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Completion(args = {"%player%"}, index = 0)
|
||||
@Completion(args = {"on", "off"}, index = 1)
|
||||
@Subcommand(permission = "datura.cage", args = {Player.class, String.class})
|
||||
public void cagePlayer(final CommandSender sender, final Player player, final String string)
|
||||
{
|
||||
switch (string.toLowerCase())
|
||||
{
|
||||
case "on" ->
|
||||
{
|
||||
((Datura) getPlugin()).getCager()
|
||||
.cagePlayer(player.getUniqueId());
|
||||
sender.sendPlainMessage("Caged " + player.getName() + ".");
|
||||
}
|
||||
case "off" ->
|
||||
{
|
||||
((Datura) getPlugin()).getCager()
|
||||
.uncagePlayer(player.getUniqueId());
|
||||
sender.sendPlainMessage("Liberated " + player.getName() + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Completion(args = {"[material]"}, index = 2)
|
||||
@Subcommand(permission = "datura.cage.custom", args = {Player.class, String.class, Material.class})
|
||||
public void cagePlayer(final CommandSender sender, final Player player, final String string,
|
||||
final Material material)
|
||||
{
|
||||
switch (string.toLowerCase())
|
||||
{
|
||||
case "on" ->
|
||||
{
|
||||
((Datura) getPlugin()).getCager()
|
||||
.cagePlayer(player.getUniqueId(), material);
|
||||
sender.sendPlainMessage("Caged " + player.getName() + ".");
|
||||
}
|
||||
case "off" ->
|
||||
{
|
||||
((Datura) getPlugin()).getCager()
|
||||
.uncagePlayer(player.getUniqueId());
|
||||
sender.sendPlainMessage("Liberated " + player.getName() + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package me.totalfreedom.datura.cmd;
|
||||
|
||||
import me.totalfreedom.command.CommandBase;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Info;
|
||||
import me.totalfreedom.command.annotation.Permissive;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import me.totalfreedom.datura.Datura;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@Completion(args = {"%player%"}, index = 0)
|
||||
@Info(name = "kick", description = "Kick a player from the server.", usage = "/kick <player>")
|
||||
@Permissive(perm = "datura.kick")
|
||||
public class KickCommand extends CommandBase
|
||||
{
|
||||
protected KickCommand(final Datura plugin)
|
||||
{
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Subcommand(permission = "datura.kick", args = {Player.class})
|
||||
public void kick(final Player player)
|
||||
{
|
||||
player.kickPlayer("You have been kicked from the server.");
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package me.totalfreedom.datura.cmd;
|
||||
|
||||
import me.totalfreedom.command.Commander;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Info;
|
||||
import me.totalfreedom.command.annotation.Permissive;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import me.totalfreedom.datura.Datura;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Info(name = "locker", description = "Lock a player, preventing them from interacting with their game client.",
|
||||
usage = "/locker <player> <on|off>", aliases = {"lock", "lockup"})
|
||||
@Permissive(perm = "datura.locker")
|
||||
public final class LockerCommand extends Commander
|
||||
{
|
||||
public LockerCommand(final @NotNull Datura plugin)
|
||||
{
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Completion(args = {"%player%"}, index = 0)
|
||||
@Completion(args = {"on", "off"}, index = 1)
|
||||
@Subcommand(permission = "datura.locker", args = {Player.class, String.class})
|
||||
public void lockPlayer(final CommandSender sender, final Player player, final String string)
|
||||
{
|
||||
if (string.equalsIgnoreCase("on"))
|
||||
{
|
||||
((Datura) getPlugin()).getLocker()
|
||||
.lock(player);
|
||||
|
||||
sender.sendPlainMessage("Locked " + player.getName() + ".");
|
||||
} else if (string.equalsIgnoreCase("off"))
|
||||
{
|
||||
((Datura) getPlugin()).getLocker()
|
||||
.unlock(player);
|
||||
sender.sendPlainMessage("Unlocked " + player.getName() + ".");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,10 @@
|
||||
package me.totalfreedom.datura.perms;
|
||||
|
||||
import me.totalfreedom.security.perm.Node;
|
||||
import me.totalfreedom.security.perm.NodeType;
|
||||
import me.totalfreedom.security.Node;
|
||||
import me.totalfreedom.security.NodeType;
|
||||
|
||||
public class DefaultNodes
|
||||
{
|
||||
private DefaultNodes() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static final Node OP = new PermissionNodeBuilder()
|
||||
.key("freedom.master_key")
|
||||
.value(true)
|
||||
@ -16,7 +12,6 @@ public class DefaultNodes
|
||||
.negated(false)
|
||||
.wildcard(true)
|
||||
.build();
|
||||
|
||||
public static final Node NON_OP = new PermissionNodeBuilder()
|
||||
.key("freedom.default")
|
||||
.value(true)
|
||||
@ -24,7 +19,6 @@ public class DefaultNodes
|
||||
.negated(false)
|
||||
.wildcard(false)
|
||||
.build();
|
||||
|
||||
public static final Node ALL = new PermissionNodeBuilder()
|
||||
.key("*")
|
||||
.value(true)
|
||||
@ -32,7 +26,6 @@ public class DefaultNodes
|
||||
.negated(false)
|
||||
.wildcard(true)
|
||||
.build();
|
||||
|
||||
public static final Node NONE = new PermissionNodeBuilder()
|
||||
.key("freedom.none")
|
||||
.value(true)
|
||||
@ -40,4 +33,9 @@ public class DefaultNodes
|
||||
.negated(false)
|
||||
.wildcard(false)
|
||||
.build();
|
||||
|
||||
private DefaultNodes()
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package me.totalfreedom.datura.perms;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.security.perm.Group;
|
||||
import me.totalfreedom.security.perm.Node;
|
||||
import me.totalfreedom.security.Group;
|
||||
import me.totalfreedom.security.Node;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
@ -44,6 +44,12 @@ public class FreedomGroup implements Group
|
||||
this.attachment = new PermissionAttachment(CommonsBase.getInstance(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId()
|
||||
{
|
||||
return UUID.nameUUIDFromBytes(getName().toString()
|
||||
.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getName()
|
||||
@ -81,12 +87,6 @@ public class FreedomGroup implements Group
|
||||
return isHidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId()
|
||||
{
|
||||
return UUID.nameUUIDFromBytes(getName().toString().getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Node> permissions()
|
||||
{
|
||||
@ -109,7 +109,8 @@ public class FreedomGroup implements Group
|
||||
public boolean isPermissionSet(@NotNull final String name)
|
||||
{
|
||||
final Node node = permissions().stream()
|
||||
.filter(n -> n.key().equalsIgnoreCase(name))
|
||||
.filter(n -> n.key()
|
||||
.equalsIgnoreCase(name))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@ -121,7 +122,8 @@ public class FreedomGroup implements Group
|
||||
{
|
||||
final Node node = permissions()
|
||||
.stream()
|
||||
.filter(n -> n.bukkit().equals(perm))
|
||||
.filter(n -> n.bukkit()
|
||||
.equals(perm))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@ -132,7 +134,8 @@ public class FreedomGroup implements Group
|
||||
public boolean hasPermission(@NotNull final String name)
|
||||
{
|
||||
final Node node = permissions().stream()
|
||||
.filter(n -> n.key().equalsIgnoreCase(name))
|
||||
.filter(n -> n.key()
|
||||
.equalsIgnoreCase(name))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@ -144,7 +147,8 @@ public class FreedomGroup implements Group
|
||||
{
|
||||
final Node node = permissions()
|
||||
.stream()
|
||||
.filter(n -> n.bukkit().equals(perm))
|
||||
.filter(n -> n.bukkit()
|
||||
.equals(perm))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@ -152,19 +156,19 @@ public class FreedomGroup implements Group
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a permission to the relative PermissionAttachment for this group.
|
||||
* This method is not thread-safe and should not be called asynchronously.
|
||||
* Adds a permission to the relative PermissionAttachment for this group. This method is not thread-safe and should
|
||||
* not be called asynchronously.
|
||||
* <p>
|
||||
* This method is only here for compatibility with the Bukkit API.
|
||||
*
|
||||
* @param plugin The plugin responsible for this attachment. May not be null
|
||||
* or disabled.
|
||||
* @param plugin The plugin responsible for this attachment. May not be null or disabled.
|
||||
* @param name Name of the permission to attach
|
||||
* @param value Value of the permission
|
||||
* @return This group's PermissionAttachment.
|
||||
*/
|
||||
@Override
|
||||
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name, final boolean value)
|
||||
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
|
||||
final boolean value)
|
||||
{
|
||||
attachment.setPermission(name, value);
|
||||
return attachment;
|
||||
@ -177,7 +181,8 @@ public class FreedomGroup implements Group
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name, final boolean value, final int ticks)
|
||||
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
|
||||
final boolean value, final int ticks)
|
||||
{
|
||||
attachment.setPermission(name, value);
|
||||
return attachment;
|
||||
|
@ -3,7 +3,7 @@ package me.totalfreedom.datura.perms;
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.datura.Datura;
|
||||
import me.totalfreedom.datura.user.SimpleUserData;
|
||||
import me.totalfreedom.security.perm.Node;
|
||||
import me.totalfreedom.security.Node;
|
||||
import me.totalfreedom.user.User;
|
||||
import me.totalfreedom.user.UserData;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@ -23,9 +23,9 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The superinterface User extends PermissionHolder,
|
||||
* which is an extension of {@link org.bukkit.permissions.Permissible}.
|
||||
* This means that our permission data can be interchanged with other permission plugins.
|
||||
* The superinterface User extends PermissionHolder, which is an extension of
|
||||
* {@link org.bukkit.permissions.Permissible}. This means that our permission data can be interchanged with other
|
||||
* permission plugins.
|
||||
*/
|
||||
public class FreedomUser implements User
|
||||
{
|
||||
@ -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());
|
||||
@ -64,10 +64,23 @@ public class FreedomUser implements User
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserData getUserData() {
|
||||
public UserData getUserData()
|
||||
{
|
||||
return userData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName()
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline()
|
||||
{
|
||||
return Bukkit.getPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId()
|
||||
{
|
||||
@ -96,18 +109,6 @@ public class FreedomUser implements User
|
||||
return permissions.remove(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName()
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline()
|
||||
{
|
||||
return Bukkit.getPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NotNull final String name)
|
||||
{
|
||||
@ -137,7 +138,8 @@ public class FreedomUser implements User
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name, final boolean value)
|
||||
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
|
||||
final boolean value)
|
||||
{
|
||||
final Player player = Bukkit.getPlayer(uuid);
|
||||
if (player != null)
|
||||
@ -161,7 +163,8 @@ public class FreedomUser implements User
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name, final boolean value, final int ticks)
|
||||
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
|
||||
final boolean value, final int ticks)
|
||||
{
|
||||
final Player player = Bukkit.getPlayer(uuid);
|
||||
if (player != null)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.totalfreedom.datura.perms;
|
||||
|
||||
import me.totalfreedom.security.perm.Node;
|
||||
import me.totalfreedom.security.perm.NodeType;
|
||||
import me.totalfreedom.security.Node;
|
||||
import me.totalfreedom.security.NodeType;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
@ -9,21 +9,23 @@ record PermissionNode(String key,
|
||||
boolean value,
|
||||
long expiry,
|
||||
NodeType type,
|
||||
boolean wildcard,
|
||||
boolean negated) implements Node
|
||||
boolean wildcard) implements Node
|
||||
{
|
||||
|
||||
@Override
|
||||
public Permission bukkit()
|
||||
{
|
||||
return new Permission(key(),
|
||||
value() ? PermissionDefault.TRUE : PermissionDefault.FALSE);
|
||||
value()
|
||||
? PermissionDefault.TRUE
|
||||
: PermissionDefault.FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compare(final Node node)
|
||||
{
|
||||
return node.key().equalsIgnoreCase(key())
|
||||
return node.key()
|
||||
.equalsIgnoreCase(key())
|
||||
&& node.value() == value()
|
||||
&& node.type() == type();
|
||||
}
|
||||
@ -31,7 +33,7 @@ record PermissionNode(String key,
|
||||
@Override
|
||||
public boolean isExpired()
|
||||
{
|
||||
if (isPermanent())
|
||||
if (!isTemporary())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -39,15 +41,9 @@ record PermissionNode(String key,
|
||||
return System.currentTimeMillis() > expiry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermanent()
|
||||
{
|
||||
return expiry() == -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTemporary()
|
||||
{
|
||||
return !isPermanent();
|
||||
return expiry() > -1;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package me.totalfreedom.datura.perms;
|
||||
|
||||
import me.totalfreedom.security.perm.Node;
|
||||
import me.totalfreedom.security.perm.NodeBuilder;
|
||||
import me.totalfreedom.security.perm.NodeType;
|
||||
import me.totalfreedom.security.Node;
|
||||
import me.totalfreedom.security.NodeBuilder;
|
||||
import me.totalfreedom.security.NodeType;
|
||||
|
||||
public class PermissionNodeBuilder implements NodeBuilder
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ package me.totalfreedom.datura.punishment;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.service.Service;
|
||||
import me.totalfreedom.utils.Shaper;
|
||||
import me.totalfreedom.utils.ShapeUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@ -14,8 +14,8 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@ -28,10 +28,11 @@ public class Cager extends Service
|
||||
|
||||
public Cager()
|
||||
{
|
||||
super("cage_service");
|
||||
super("cager-service");
|
||||
this.cagedPlayers = new HashSet<>();
|
||||
this.cageLocations = new HashMap<>();
|
||||
Bukkit.getPluginManager().registerEvents(new CageListener(), CommonsBase.getInstance());
|
||||
Bukkit.getPluginManager()
|
||||
.registerEvents(new CageListener(), CommonsBase.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,6 +50,38 @@ public class Cager extends Service
|
||||
cageLocations.put(uuid, createCage(player.getLocation(), Material.GLASS));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a cube centered around the passed location, made of the provided material. This method
|
||||
* returns the passed location object. We use the {@link ShapeUtils} class to generate the cube, which allows us to
|
||||
* define custom shapes using {@link DoubleUnaryOperator}s.
|
||||
*
|
||||
* @param location The location to center the cube around.
|
||||
* @param material The material to use for the cube.
|
||||
* @return The center location of the cube (the passed location).
|
||||
* @see ShapeUtils
|
||||
* @see DoubleUnaryOperator
|
||||
*/
|
||||
public Location createCage(final Location location, final Material material)
|
||||
{
|
||||
final ShapeUtils shapeUtils = new ShapeUtils(location.getWorld(), 0.0, 4.0);
|
||||
final List<Location> cubed = new LinkedList<>();
|
||||
cubed.addAll(shapeUtils.generate(5, t -> t, t -> 4.0, t -> t));
|
||||
cubed.addAll(shapeUtils.generate(5, t -> t, t -> 0.0, t -> t));
|
||||
cubed.addAll(shapeUtils.generate(5, t -> 0.0, t -> t, t -> t));
|
||||
cubed.addAll(shapeUtils.generate(5, t -> 4.0, t -> t, t -> t));
|
||||
cubed.addAll(shapeUtils.generate(5, t -> t, t -> t, t -> 0.0));
|
||||
cubed.addAll(shapeUtils.generate(5, t -> t, t -> t, t -> 4.0));
|
||||
|
||||
for (final Location l : cubed)
|
||||
{
|
||||
location.getWorld()
|
||||
.getBlockAt(l)
|
||||
.setType(material);
|
||||
}
|
||||
|
||||
return location.clone(); // Return the passed location as that is the center of the cube.
|
||||
}
|
||||
|
||||
public void cagePlayer(final UUID uuid, final Material material)
|
||||
{
|
||||
final Player player = Bukkit.getPlayer(uuid);
|
||||
@ -74,8 +107,7 @@ public class Cager extends Service
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will check to make sure each caged player remains within their cage.
|
||||
* We use
|
||||
* This method will check to make sure each caged player remains within their cage. We use
|
||||
* <p>
|
||||
* <code>{@link Location#distanceSquared(Location)} * {@link Math#pow(double, double)}</code>
|
||||
* <p>
|
||||
@ -92,12 +124,14 @@ public class Cager extends Service
|
||||
final Location cageLocation = getCageLocation(player);
|
||||
|
||||
final boolean inside;
|
||||
if (!player.getWorld().equals(cageLocation.getWorld()))
|
||||
if (!player.getWorld()
|
||||
.equals(cageLocation.getWorld()))
|
||||
{
|
||||
inside = false;
|
||||
} else
|
||||
{
|
||||
inside = player.getLocation().distanceSquared(cageLocation) > (Math.pow(2.5, 2.0));
|
||||
inside = player.getLocation()
|
||||
.distanceSquared(cageLocation) > (Math.pow(2.5, 2.0));
|
||||
}
|
||||
|
||||
if (!inside)
|
||||
@ -119,43 +153,13 @@ public class Cager extends Service
|
||||
return cageLocations.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a cube centered around the passed location,
|
||||
* made of the provided material. This method returns the passed location object.
|
||||
* We use the {@link Shaper} class to generate the cube, which allows us to define
|
||||
* custom shapes using {@link DoubleUnaryOperator}s.
|
||||
*
|
||||
* @param location The location to center the cube around.
|
||||
* @param material The material to use for the cube.
|
||||
* @return The center location of the cube (the passed location).
|
||||
* @see Shaper
|
||||
* @see DoubleUnaryOperator
|
||||
*/
|
||||
public Location createCage(final Location location, final Material material)
|
||||
{
|
||||
final Shaper shaper = new Shaper(location.getWorld(), 0.0, 4.0);
|
||||
final List<Location> cubed = new LinkedList<>();
|
||||
cubed.addAll(shaper.generate(5, t -> t, t -> 4.0, t -> t));
|
||||
cubed.addAll(shaper.generate(5, t -> t, t -> 0.0, t -> t));
|
||||
cubed.addAll(shaper.generate(5, t -> 0.0, t -> t, t -> t));
|
||||
cubed.addAll(shaper.generate(5, t -> 4.0, t -> t, t -> t));
|
||||
cubed.addAll(shaper.generate(5, t -> t, t -> t, t -> 0.0));
|
||||
cubed.addAll(shaper.generate(5, t -> t, t -> t, t -> 4.0));
|
||||
|
||||
for (final Location l : cubed)
|
||||
{
|
||||
location.getWorld().getBlockAt(l).setType(material);
|
||||
}
|
||||
|
||||
return location.clone(); // Return the passed location as that is the center of the cube.
|
||||
}
|
||||
|
||||
private final class CageListener implements Listener
|
||||
{
|
||||
@EventHandler
|
||||
public void blockBreakEvent(final BlockBreakEvent event)
|
||||
{
|
||||
if (cagedPlayers.contains(event.getPlayer().getUniqueId()))
|
||||
if (cagedPlayers.contains(event.getPlayer()
|
||||
.getUniqueId()))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
}
|
||||
@ -164,9 +168,11 @@ public class Cager extends Service
|
||||
@EventHandler
|
||||
public void playerLeaveEvent(final PlayerQuitEvent event)
|
||||
{
|
||||
if (cagedPlayers.contains(event.getPlayer().getUniqueId()))
|
||||
if (cagedPlayers.contains(event.getPlayer()
|
||||
.getUniqueId()))
|
||||
{
|
||||
uncagePlayer(event.getPlayer().getUniqueId());
|
||||
uncagePlayer(event.getPlayer()
|
||||
.getUniqueId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ public class Halter implements Listener
|
||||
@EventHandler
|
||||
public void playerMove(final PlayerMoveEvent event)
|
||||
{
|
||||
if (haltedPlayers.contains(event.getPlayer().getUniqueId()))
|
||||
if (haltedPlayers.contains(event.getPlayer()
|
||||
.getUniqueId()))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
@ -22,15 +22,23 @@ public class Locker extends Service
|
||||
super("locker-service");
|
||||
}
|
||||
|
||||
public void lock(final UUID uuid)
|
||||
public void lock(final Player player)
|
||||
{
|
||||
lockedPlayers.add(uuid);
|
||||
lockedPlayers.add(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void unlock(final Player player)
|
||||
{
|
||||
lockedPlayers.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
lockedPlayers.removeIf(uuid -> !CommonsBase.getInstance().getServer().getOfflinePlayer(uuid).isOnline());
|
||||
lockedPlayers.removeIf(uuid -> !CommonsBase.getInstance()
|
||||
.getServer()
|
||||
.getOfflinePlayer(uuid)
|
||||
.isOnline());
|
||||
|
||||
for (final UUID uuid : lockedPlayers)
|
||||
{
|
||||
@ -43,8 +51,10 @@ public class Locker extends Service
|
||||
|
||||
private void lockingMethod(@NotNull final Player player)
|
||||
{
|
||||
final double x = player.getLocation().getX();
|
||||
final double z = player.getLocation().getZ();
|
||||
final double x = player.getLocation()
|
||||
.getX();
|
||||
final double z = player.getLocation()
|
||||
.getZ();
|
||||
|
||||
if ((x / z % 0.001) < 1)
|
||||
{
|
||||
@ -65,10 +75,12 @@ public class Locker extends Service
|
||||
|
||||
player.openInventory(Bukkit.createInventory(null, 54));
|
||||
player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN);
|
||||
player.teleport(player.getLocation().clone());
|
||||
player.teleport(player.getLocation()
|
||||
.clone());
|
||||
|
||||
final SplittableRandom random = new SplittableRandom();
|
||||
player.getEyeLocation().add(new Vector(
|
||||
player.getEyeLocation()
|
||||
.add(new Vector(
|
||||
random.nextDouble(-1.0, 1.0),
|
||||
random.nextDouble(-1.0, 1.0),
|
||||
random.nextDouble(-1.0, 1.0)
|
||||
|
@ -1,89 +0,0 @@
|
||||
package me.totalfreedom.datura.sql;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.datura.banning.SimpleBan;
|
||||
import me.totalfreedom.security.ban.Ban;
|
||||
import me.totalfreedom.security.ban.BanID;
|
||||
import me.totalfreedom.sql.SQL;
|
||||
import me.totalfreedom.utils.FreedomLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DBBan
|
||||
{
|
||||
private final SQL sql;
|
||||
|
||||
public DBBan(final SQL sql)
|
||||
{
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
public CompletableFuture<Ban> fromSQL(final BanID id)
|
||||
{
|
||||
return sql.executeQuery("SELECT * FROM bans WHERE id = ?", id.getID())
|
||||
.thenApplyAsync(result ->
|
||||
{
|
||||
try
|
||||
{
|
||||
if (result.next())
|
||||
{
|
||||
final UUID uuid = UUID.fromString(result.getString("uuid"));
|
||||
final Instant timestamp = Instant.parse(result.getString("timestamp"));
|
||||
|
||||
final Instant expiry;
|
||||
final String ex = result.getString("expiry");
|
||||
if (ex.equals("-1"))
|
||||
{
|
||||
expiry = null;
|
||||
} else
|
||||
{
|
||||
expiry = Instant.parse(ex);
|
||||
}
|
||||
|
||||
return new SimpleBan(uuid,
|
||||
result.getString("reason"),
|
||||
result.getString("issuer"),
|
||||
timestamp,
|
||||
expiry);
|
||||
}
|
||||
} catch (SQLException e)
|
||||
{
|
||||
FreedomLogger.getLogger("Datura")
|
||||
.error(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
}
|
||||
|
||||
public void addBan(final Ban ban)
|
||||
{
|
||||
sql.executeUpdate("INSERT INTO bans (id, uuid, reason, issuer, timestamp, expiry) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
ban.getBanID().getID(),
|
||||
ban.getOffenderID().toString(),
|
||||
ban.getReason(),
|
||||
ban.getBanIssuer(),
|
||||
ban.getCreationTime().toString(),
|
||||
(ban.getExpiry() != null ? ban.getExpiry().toString() : "-1")
|
||||
);
|
||||
}
|
||||
|
||||
public boolean hasEntry(final UUID uuid) {
|
||||
return sql.executeQuery("SELECT * FROM bans WHERE uuid = ?", uuid.toString())
|
||||
.thenApplyAsync(result ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return result.next();
|
||||
} catch (SQLException e)
|
||||
{
|
||||
FreedomLogger.getLogger("Datura")
|
||||
.error(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync())
|
||||
.join();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package me.totalfreedom.datura.sql;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.sql.SQL;
|
||||
import me.totalfreedom.utils.container.Identity;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
@ -13,98 +14,145 @@ import java.util.concurrent.CompletionException;
|
||||
|
||||
public class MySQL implements SQL
|
||||
{
|
||||
private String url = "jdbc:mysql://";
|
||||
/**
|
||||
* Using StringBuilder for finality.
|
||||
*/
|
||||
private final StringBuilder url = new StringBuilder("jdbc:mysql://");
|
||||
|
||||
public MySQL(final String host, final int port, final String database) {
|
||||
url += host + ":" + port + "/" + database;
|
||||
public MySQL(final String host, final int port, final String database)
|
||||
{
|
||||
url.append(host)
|
||||
.append(':')
|
||||
.append(port)
|
||||
.append('/')
|
||||
.append(database);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds credentials to the MySQL URL.
|
||||
* If the URL already contains credentials, they will be overwritten.
|
||||
* Adds credentials to the MySQL URL. If the URL already contains credentials, they will be overwritten.
|
||||
*
|
||||
* @param username The username to add
|
||||
* @param password The password to add
|
||||
*/
|
||||
public void addCredentials(final String username, final String password) {
|
||||
if (url.contains("?user=")) {
|
||||
url = url.split("\\x3f")[0];
|
||||
}
|
||||
|
||||
url += "?user=" + username + "&password=" + password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Connection> getConnection(final String url)
|
||||
public void addCredentials(final String username, final String password)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return DriverManager.getConnection(url);
|
||||
} catch (SQLException ex) {
|
||||
throw new CompletionException("Failed to connect to the database: "
|
||||
+ url + "\n", ex);
|
||||
if (url.toString()
|
||||
.contains("?user="))
|
||||
{
|
||||
final String split = url.toString()
|
||||
.split("\\x3f")[0];
|
||||
url.setLength(0);
|
||||
url.append(split);
|
||||
}
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
|
||||
url.append("?user=")
|
||||
.append(username)
|
||||
.append("&password=")
|
||||
.append(password);
|
||||
}
|
||||
|
||||
public CompletableFuture<ResultSet> getRow(final String table, final String column, final Identity identity)
|
||||
{
|
||||
return executeQuery("SELECT * FROM ? WHERE ? = ?", table, column, identity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args)
|
||||
{
|
||||
return getConnection(url)
|
||||
.thenApplyAsync(connection -> {
|
||||
try {
|
||||
return getConnection()
|
||||
.thenApplyAsync(connection ->
|
||||
{
|
||||
try
|
||||
{
|
||||
final PreparedStatement statement = connection.prepareStatement(query);
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
for (int i = 0; i < args.length; i++)
|
||||
{
|
||||
statement.setObject(i + 1, args[i]);
|
||||
}
|
||||
return statement;
|
||||
} catch (SQLException ex) {
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException("Failed to prepare statement: "
|
||||
+ query + "\n", ex);
|
||||
}
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
private CompletableFuture<Connection> getConnection()
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return DriverManager.getConnection(url.toString());
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException("Failed to connect to the database: "
|
||||
+ url.toString() + "\n", ex);
|
||||
}
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResultSet> executeQuery(final String query, final Object... args)
|
||||
{
|
||||
return prepareStatement(query, args)
|
||||
.thenApplyAsync(statement -> {
|
||||
try {
|
||||
.thenApplyAsync(statement ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return statement.executeQuery();
|
||||
} catch (SQLException ex) {
|
||||
throw new CompletionException("Failed to retrieve a result set from query: "
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException(
|
||||
"Failed to retrieve a result set from query: "
|
||||
+ query + "\n", ex);
|
||||
}
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> executeUpdate(final String query, final Object... args)
|
||||
{
|
||||
return prepareStatement(query, args)
|
||||
.thenApplyAsync(statement -> {
|
||||
try {
|
||||
.thenApplyAsync(statement ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return statement.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException("Failed to execute update: "
|
||||
+ query + "\n", ex);
|
||||
}
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> execute(final String query, final Object... args)
|
||||
{
|
||||
return prepareStatement(query, args)
|
||||
.thenApplyAsync(statement -> {
|
||||
try {
|
||||
.thenApplyAsync(statement ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return statement.execute();
|
||||
} catch (SQLException ex) {
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException("Failed to execute statement: "
|
||||
+ query + "\n", ex);
|
||||
}
|
||||
}, CommonsBase.getInstance().getExecutor().getAsync());
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -113,9 +161,11 @@ public class MySQL implements SQL
|
||||
final StringBuilder query = new StringBuilder();
|
||||
|
||||
query.append("CREATE TABLE IF NOT EXISTS ? (");
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
for (int i = 0; i < columns.length; i++)
|
||||
{
|
||||
query.append("?");
|
||||
if (i != columns.length - 1) {
|
||||
if (i != columns.length - 1)
|
||||
{
|
||||
query.append(", ");
|
||||
}
|
||||
}
|
||||
@ -123,4 +173,89 @@ public class MySQL implements SQL
|
||||
|
||||
return execute(query.toString(), table, columns);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> getColumn(final String table, final String column, final String key,
|
||||
final Identity identity, final Class<T> type)
|
||||
{
|
||||
return executeQuery("SELECT ? FROM ? WHERE ? = ?", column, table, key, identity.getId())
|
||||
.thenApplyAsync(resultSet ->
|
||||
{
|
||||
try
|
||||
{
|
||||
if (resultSet.next())
|
||||
{
|
||||
return resultSet.getObject(column, type);
|
||||
}
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
throw new CompletionException(
|
||||
"Failed to retrieve column: " + column + " from table: " + table + " " +
|
||||
"where primary key: " + key + " is equal to: " + identity.getId() + "\n",
|
||||
ex);
|
||||
}
|
||||
return null;
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> updateColumn(final String table, final String column, final Object value,
|
||||
final String key, final Identity identity)
|
||||
{
|
||||
return executeUpdate("UPDATE ? SET ? = ? WHERE ? = ?", table, column, value, key, identity.getId())
|
||||
.thenApplyAsync(result -> result > 0, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> deleteRow(final String table, final String key, final Identity identity)
|
||||
{
|
||||
return executeUpdate("DELETE FROM ? WHERE ? = ?", table, key, identity.getId())
|
||||
.thenApplyAsync(result -> result > 0, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getAsync());
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> insertRow(final String table, final Object... values)
|
||||
{
|
||||
final StringBuilder query = new StringBuilder();
|
||||
query.append("INSERT INTO ? VALUES (");
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
query.append("?");
|
||||
if (i != values.length - 1)
|
||||
{
|
||||
query.append(", ");
|
||||
}
|
||||
}
|
||||
query.append(")");
|
||||
return execute(query.toString(), table, values);
|
||||
}
|
||||
|
||||
public CompletableFuture<Boolean> insertRow(final String table, final String[] columns, final Object... values)
|
||||
{
|
||||
final StringBuilder query = new StringBuilder();
|
||||
query.append("INSERT INTO ? (");
|
||||
for (int i = 0; i < columns.length; i++)
|
||||
{
|
||||
query.append("?");
|
||||
if (i != columns.length - 1)
|
||||
{
|
||||
query.append(", ");
|
||||
}
|
||||
}
|
||||
query.append(") VALUES (");
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
query.append("?");
|
||||
if (i != values.length - 1)
|
||||
{
|
||||
query.append(", ");
|
||||
}
|
||||
}
|
||||
query.append(")");
|
||||
return execute(query.toString(), table, columns, values);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
14
Datura/src/main/java/me/totalfreedom/datura/sql/Result.java
Normal file
14
Datura/src/main/java/me/totalfreedom/datura/sql/Result.java
Normal file
@ -0,0 +1,14 @@
|
||||
package me.totalfreedom.datura.sql;
|
||||
|
||||
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
|
||||
/**
|
||||
* Represents a single result from a result set.
|
||||
*/
|
||||
@Immutable
|
||||
public record Result(String name, Object value)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,128 @@
|
||||
package me.totalfreedom.datura.user;
|
||||
|
||||
import me.totalfreedom.economy.EconomicEntity;
|
||||
import me.totalfreedom.economy.EconomicEntityData;
|
||||
|
||||
/**
|
||||
* Represents the server's economy holder.
|
||||
* <br>
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* <br>
|
||||
* Please be aware, if the server's economy falls below 0, it will have drastic consequences.
|
||||
*/
|
||||
public class ServerEconomyHolder implements EconomicEntity, EconomicEntityData
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
balance += amount;
|
||||
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)
|
||||
{
|
||||
balance -= amount;
|
||||
return balance;
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ package me.totalfreedom.datura.user;
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.datura.event.UserDataUpdateEvent;
|
||||
import me.totalfreedom.datura.perms.FreedomUser;
|
||||
import me.totalfreedom.security.perm.Group;
|
||||
import me.totalfreedom.security.Group;
|
||||
import me.totalfreedom.sql.SQL;
|
||||
import me.totalfreedom.user.User;
|
||||
import me.totalfreedom.user.UserData;
|
||||
import me.totalfreedom.utils.FreedomLogger;
|
||||
import me.totalfreedom.utils.logging.FreedomLogger;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -26,9 +26,7 @@ public class SimpleUserData implements UserData
|
||||
private final UserDataUpdateEvent event = new UserDataUpdateEvent(this);
|
||||
private Group group;
|
||||
private long playtime;
|
||||
private boolean frozen;
|
||||
private boolean canInteract;
|
||||
private boolean caged;
|
||||
private AtomicLong balance;
|
||||
private boolean transactionsFrozen;
|
||||
|
||||
@ -38,7 +36,9 @@ public class SimpleUserData implements UserData
|
||||
this.username = player.getName();
|
||||
this.user = new FreedomUser(player);
|
||||
|
||||
CommonsBase.getInstance().getEventBus().addEvent(event);
|
||||
CommonsBase.getInstance()
|
||||
.getEventBus()
|
||||
.addEvent(event);
|
||||
}
|
||||
|
||||
private SimpleUserData(
|
||||
@ -47,9 +47,7 @@ public class SimpleUserData implements UserData
|
||||
final User user,
|
||||
final Group group,
|
||||
final long playtime,
|
||||
final boolean frozen,
|
||||
final boolean canInteract,
|
||||
final boolean caged,
|
||||
final long balance,
|
||||
final boolean transactionsFrozen)
|
||||
{
|
||||
@ -58,9 +56,7 @@ public class SimpleUserData implements UserData
|
||||
this.user = user;
|
||||
this.group = group;
|
||||
this.playtime = playtime;
|
||||
this.frozen = frozen;
|
||||
this.canInteract = canInteract;
|
||||
this.caged = caged;
|
||||
this.balance = new AtomicLong(balance);
|
||||
this.transactionsFrozen = transactionsFrozen;
|
||||
}
|
||||
@ -91,16 +87,17 @@ public class SimpleUserData implements UserData
|
||||
.getGroup(g);
|
||||
|
||||
final long playtime = result.getLong("playtime");
|
||||
final boolean frozen = result.getBoolean("frozen");
|
||||
final boolean canInteract = result.getBoolean("canInteract");
|
||||
final boolean caged = result.getBoolean("caged");
|
||||
final long balance = result.getLong("balance");
|
||||
final boolean transactionsFrozen = result.getBoolean("transactionsFrozen");
|
||||
return new SimpleUserData(u, username, user, group, playtime, frozen, canInteract, caged, balance, transactionsFrozen);
|
||||
|
||||
return new SimpleUserData(u, username, user, group, playtime,
|
||||
canInteract, balance, transactionsFrozen);
|
||||
}
|
||||
} catch (SQLException ex)
|
||||
{
|
||||
final String sb = "An error occurred while trying to retrieve user data for UUID " +
|
||||
final String sb = "An error occurred while trying to retrieve user data for" +
|
||||
" UUID " +
|
||||
uuid +
|
||||
" from the database." +
|
||||
"\nCaused by: " +
|
||||
@ -114,6 +111,7 @@ public class SimpleUserData implements UserData
|
||||
|
||||
final Player player = Bukkit.getPlayer(UUID.fromString(uuid));
|
||||
if (player == null) throw new IllegalStateException("Player should be online but they are not!");
|
||||
|
||||
return new SimpleUserData(player);
|
||||
}, CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
@ -179,19 +177,6 @@ public class SimpleUserData implements UserData
|
||||
this.playtime = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFrozen()
|
||||
{
|
||||
return frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(final boolean frozen)
|
||||
{
|
||||
event.ping();
|
||||
this.frozen = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteract()
|
||||
{
|
||||
@ -205,19 +190,6 @@ public class SimpleUserData implements UserData
|
||||
this.canInteract = canInteract;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaged()
|
||||
{
|
||||
return caged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaged(final boolean caged)
|
||||
{
|
||||
event.ping();
|
||||
this.caged = caged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areTransactionsFrozen()
|
||||
{
|
||||
@ -230,6 +202,12 @@ public class SimpleUserData implements UserData
|
||||
return balance.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBalance(final long newBalance)
|
||||
{
|
||||
balance.set(newBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long addToBalance(final long amount)
|
||||
{
|
||||
@ -241,10 +219,4 @@ public class SimpleUserData implements UserData
|
||||
{
|
||||
return balance.addAndGet(-amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBalance(final long newBalance)
|
||||
{
|
||||
balance.set(newBalance);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,25 @@
|
||||
package me.totalfreedom.fossil;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import me.totalfreedom.base.Registration;
|
||||
import me.totalfreedom.fossil.trail.Trailer;
|
||||
import me.totalfreedom.service.SubscriptionProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Fossil extends JavaPlugin
|
||||
{
|
||||
private final Trailer trailer = new Trailer();
|
||||
private final Registration registration = CommonsBase.getInstance()
|
||||
.getRegistrations();
|
||||
|
||||
@Override
|
||||
public void onEnable()
|
||||
{
|
||||
CommonsBase.getInstance()
|
||||
.getRegistrations()
|
||||
.getModuleRegistry()
|
||||
registration.getModuleRegistry()
|
||||
.addModule(this);
|
||||
|
||||
registration.getServiceTaskRegistry()
|
||||
.registerService(
|
||||
SubscriptionProvider.syncService(this, trailer));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* The type of the pad, defined by {@link #padType}, will determine how the player is bounced.
|
||||
* <br>
|
||||
* For type {@link PadType#NORMAL}, the player will be bounced if the face is {@link BlockFace#UP}.
|
||||
* <br>
|
||||
* For type {@link PadType#SIDES}, the player will be bounced if the face is not {@link BlockFace#UP} or
|
||||
* {@link BlockFace#DOWN}.
|
||||
* <br>
|
||||
* For type {@link PadType#ALL}, the player will be bounced regardless of the face.
|
||||
* <br>
|
||||
* For type {@link PadType#EXTREME}, the player will be bounced with a velocity based on the formula:
|
||||
* <br>
|
||||
* <span color=#f07a21><code>(((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))</code></span>
|
||||
* <br>
|
||||
* For type {@link PadType#SPACE_CADET}, the player will be bounced with a velocity based on the formula:
|
||||
* <br>
|
||||
* <span color=#f07a21><code>Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
|
||||
* Math.exp(accel)))</code></span>
|
||||
* <br>
|
||||
* where <span color=#f07a21><code>y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)</code></span> and <span
|
||||
* color=#f07a21><code>accel = Math.sqrt(2 * 9.81 * y)</code></span>
|
||||
* <br>
|
||||
* <br>
|
||||
* <b>NOTE:</b> 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:
|
||||
* <br>
|
||||
* <span color=#f07a21><code>(BlockFace direction + Player velocity * -1) * velocity</code></span>
|
||||
* <br>
|
||||
* <br>
|
||||
* 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:
|
||||
* <br>
|
||||
* <span color=#f07a21><code>(((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))</code></span>
|
||||
* <br>
|
||||
* <br>
|
||||
* <b>NOTE:</b> 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:
|
||||
* <br>
|
||||
* <span color=#f07a21><code>Math.round(Math.abs((accel * 100.0) + Math.pow(y, Math.floor(accel)) /
|
||||
* Math.exp(accel)))</code></span>
|
||||
* <br>
|
||||
* where <span color=#f07a21><code>y = Math.abs(random.nextGaussian(12, 5) * 0.5 + 0.5)</code></span> and
|
||||
* <span color=#f07a21><code>accel = Math.sqrt(2 * 9.81 * y)</code></span>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
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<UUID, BouncyPad> 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<UUID, BouncyPad> 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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -21,37 +21,42 @@
|
||||
|
||||
package me.totalfreedom.fossil.cmd;
|
||||
|
||||
import me.totalfreedom.command.CommandBase;
|
||||
import me.totalfreedom.command.Commander;
|
||||
import me.totalfreedom.command.annotation.Base;
|
||||
import me.totalfreedom.command.annotation.Info;
|
||||
import me.totalfreedom.command.annotation.Permissive;
|
||||
import me.totalfreedom.utils.FreedomMiniMessage;
|
||||
import me.totalfreedom.utils.kyori.FreedomMiniMessage;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Info(name = "cake", description = "Gives everyone cake and broadcasts a message.", usage = "/cake")
|
||||
@Permissive(perm = "fossil.cake")
|
||||
public class CakeCommand extends CommandBase
|
||||
public class CakeCommand extends Commander
|
||||
{
|
||||
protected CakeCommand(JavaPlugin plugin)
|
||||
protected CakeCommand(final @NotNull JavaPlugin plugin)
|
||||
{
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Base
|
||||
public void cake(final CommandSender sender)
|
||||
public void broadcastCake(final CommandSender sender)
|
||||
{
|
||||
Bukkit.broadcast(FreedomMiniMessage.deserialize(true, "<rainbow>But there's no sense crying over every mistake. You just keep on trying till you run out of cake.</rainbow>"));
|
||||
Bukkit.broadcast(FreedomMiniMessage.deserialize(true,
|
||||
"<rainbow>But there's no sense crying over every mistake. You just keep on trying till you run out of " +
|
||||
"cake.</rainbow>"));
|
||||
|
||||
final ItemStack stack = new ItemStack(Material.CAKE, 1);
|
||||
final ItemMeta meta = stack.getItemMeta();
|
||||
meta.displayName(FreedomMiniMessage.deserialize(true, "<dark_gray>The <white>Lie"));
|
||||
stack.setItemMeta(meta);
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(player -> player.getInventory().addItem(stack));
|
||||
Bukkit.getOnlinePlayers()
|
||||
.forEach(player -> player.getInventory()
|
||||
.addItem(stack));
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class SimpleTransaction implements Transaction
|
||||
{
|
||||
protected final AtomicLong balance;
|
||||
private final EconomicEntity source;
|
||||
private final EconomicEntity destination;
|
||||
protected final AtomicLong balance;
|
||||
|
||||
public SimpleTransaction(final EconomicEntity source, final EconomicEntity destination, final long balance)
|
||||
{
|
||||
|
@ -1,11 +1,11 @@
|
||||
package me.totalfreedom.fossil.economy;
|
||||
|
||||
import me.totalfreedom.audience.MutableAudienceForwarder;
|
||||
import me.totalfreedom.economy.TransactionResult;
|
||||
import me.totalfreedom.economy.CompletedTransaction;
|
||||
import me.totalfreedom.economy.TransactionLogger;
|
||||
import me.totalfreedom.economy.EconomicEntity;
|
||||
import me.totalfreedom.utils.FreedomLogger;
|
||||
import me.totalfreedom.economy.TransactionLogger;
|
||||
import me.totalfreedom.economy.TransactionResult;
|
||||
import me.totalfreedom.utils.logging.FreedomLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public class SimpleTransactionLogger implements TransactionLogger
|
||||
@ -24,7 +24,9 @@ public class SimpleTransactionLogger implements TransactionLogger
|
||||
final EconomicEntity destination = completedTransaction.getDestination();
|
||||
final long transactionAmount = completedTransaction.getBalance();
|
||||
|
||||
transactionLoggingStatementBuilder.append(resultSuccess ? "Successful" : "Unsuccessful")
|
||||
transactionLoggingStatementBuilder.append(resultSuccess
|
||||
? "Successful"
|
||||
: "Unsuccessful")
|
||||
.append(" (")
|
||||
.append(resultMessage)
|
||||
.append(") ")
|
||||
|
@ -7,16 +7,21 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
||||
public class SimpleTransactionResult implements TransactionResult
|
||||
{
|
||||
public static final TransactionResult SUCCESSFUL = new SimpleTransactionResult("Successful transaction.", true);
|
||||
public static final TransactionResult UNAUTHORIZED = new SimpleTransactionResult("Unauthorized transaction.", false);
|
||||
public static final TransactionResult AMOUNT_TOO_SMALL = new SimpleTransactionResult("Transaction balance too small.", false);
|
||||
public static final TransactionResult INSUFFICIENT_FUNDS = new SimpleTransactionResult("The source has an insufficient balance to carry out this transaction.", false);
|
||||
public static final TransactionResult UNAUTHORIZED = new SimpleTransactionResult("Unauthorized transaction.",
|
||||
false);
|
||||
public static final TransactionResult AMOUNT_TOO_SMALL = new SimpleTransactionResult(
|
||||
"Transaction balance too small.", false);
|
||||
public static final TransactionResult INSUFFICIENT_FUNDS = new SimpleTransactionResult(
|
||||
"The source has an insufficient balance to carry out this transaction.", false);
|
||||
private final String message;
|
||||
private final Component component;
|
||||
private final boolean successful;
|
||||
|
||||
public SimpleTransactionResult(final String message, final boolean successful)
|
||||
{
|
||||
this(message, Component.text(message, successful ? NamedTextColor.GREEN : NamedTextColor.RED), successful);
|
||||
this(message, Component.text(message, successful
|
||||
? NamedTextColor.GREEN
|
||||
: NamedTextColor.RED), successful);
|
||||
}
|
||||
|
||||
public SimpleTransactionResult(final String message, final Component component, final boolean successful)
|
||||
|
@ -0,0 +1,30 @@
|
||||
package me.totalfreedom.fossil.items;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class ClownfishItem extends ShopItem
|
||||
{
|
||||
public ClownfishItem()
|
||||
{
|
||||
super(Material.TROPICAL_FISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAction(final @NotNull Player user, final @Nullable Entity target)
|
||||
{
|
||||
if (target == null) return;
|
||||
|
||||
final Location location = user.getEyeLocation()
|
||||
.clone();
|
||||
final Vector vector = location.getDirection()
|
||||
.multiply(2);
|
||||
|
||||
target.setVelocity(vector);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package me.totalfreedom.fossil.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ShopItem
|
||||
{
|
||||
private final ItemStack item;
|
||||
private final ItemMeta meta;
|
||||
|
||||
protected ShopItem(final Material material)
|
||||
{
|
||||
this.item = new ItemStack(material, 1);
|
||||
|
||||
this.meta = this.item.getItemMeta();
|
||||
}
|
||||
|
||||
public abstract void runAction(@NotNull final Player user, @Nullable final Entity target);
|
||||
|
||||
public ItemStack getItem()
|
||||
{
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public ItemMeta getMeta()
|
||||
{
|
||||
return this.meta;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package me.totalfreedom.fossil.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class TrailItem extends ShopItem
|
||||
{
|
||||
public TrailItem()
|
||||
{
|
||||
super(Material.LINGERING_POTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAction(final @NotNull Player user, final @Nullable Entity target)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
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<EconomicEntity> 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();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package me.totalfreedom.fossil.shop;
|
||||
|
||||
public class Shoppe
|
||||
{
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package me.totalfreedom.fossil.shop.menus;
|
||||
|
||||
import me.totalfreedom.display.AbstractMenu;
|
||||
|
||||
public final class MainMenu extends AbstractMenu
|
||||
{
|
||||
protected MainMenu(final int size)
|
||||
{
|
||||
super(size);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package me.totalfreedom.fossil.trail;
|
||||
|
||||
import me.totalfreedom.particle.Trail;
|
||||
import me.totalfreedom.service.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Trailer extends Service
|
||||
{
|
||||
private final List<Trail> activeTrails = new ArrayList<>();
|
||||
|
||||
// Cannot be async due to interaction with the world, and API interactions MUST be synchronized.
|
||||
public Trailer()
|
||||
{
|
||||
super("trailer_service");
|
||||
}
|
||||
|
||||
public void addTrail(final Trail trail)
|
||||
{
|
||||
this.activeTrails.add(trail);
|
||||
}
|
||||
|
||||
public void removeTrail(final Trail trail)
|
||||
{
|
||||
this.activeTrails.remove(trail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
for (final Trail trail : activeTrails)
|
||||
{
|
||||
trail.spawnParticle();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class BasicTrail extends SimpleTrail
|
||||
{
|
||||
protected BasicTrail(final Player player)
|
||||
{
|
||||
super(player, TrailType.DEFAULT);
|
||||
super.setColor(Color.RED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle()
|
||||
{
|
||||
// Exit immediately if either condition is false.
|
||||
if (!isActive() || !getAssociatedPlayer().isOnline()) return;
|
||||
|
||||
// Trail is active and the player is online.
|
||||
final Particle particle = getTrailType().getType();
|
||||
final Particle.DustOptions options = new Particle.DustOptions(getColor(), 3);
|
||||
final Player player = (Player) getAssociatedPlayer();
|
||||
final Location location = player.getLocation()
|
||||
.clone()
|
||||
.subtract(0, 1, 0);
|
||||
location.getWorld()
|
||||
.spawnParticle(particle, location, 1, 0.0, 0.5, 0.0, options);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public final class FlameTrail extends SimpleTrail
|
||||
{
|
||||
public FlameTrail(final Player player)
|
||||
{
|
||||
super(player, TrailType.FLAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle()
|
||||
{
|
||||
if (!getAssociatedPlayer().isOnline() || !isActive()) return;
|
||||
|
||||
final Player player = (Player) getAssociatedPlayer();
|
||||
final Location location = player.getLocation()
|
||||
.clone()
|
||||
.subtract(0, 1, 0);
|
||||
final Vector direction = location.getDirection();
|
||||
location.getWorld()
|
||||
.spawnParticle(getTrailType().getType(), location, 0, direction.getX(), direction.getY(),
|
||||
direction.getZ(), 0.1);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class HeartTrail extends SimpleTrail
|
||||
{
|
||||
public HeartTrail(final Player player)
|
||||
{
|
||||
super(player, TrailType.HEART);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle()
|
||||
{
|
||||
if (!getAssociatedPlayer().isOnline() || !isActive()) return;
|
||||
|
||||
final Player player = (Player) getAssociatedPlayer();
|
||||
final Location location = player.getLocation()
|
||||
.clone()
|
||||
.subtract(0, 1, 0);
|
||||
location.getWorld()
|
||||
.spawnParticle(getTrailType().getType(), location, 1, 0.0, 0.5, 0.0);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import me.totalfreedom.utils.InterpolationUtils;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public final class RainbowTrail extends SimpleTrail
|
||||
{
|
||||
private Iterator<Color> currentColor;
|
||||
|
||||
protected RainbowTrail(final Player player)
|
||||
{
|
||||
super(player, TrailType.DEFAULT);
|
||||
setColors(InterpolationUtils.rainbow(40 % 7));
|
||||
this.currentColor = getColors().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle()
|
||||
{
|
||||
// Exit immediately if either case is false.
|
||||
if (!isActive() || !getAssociatedPlayer().isOnline()) return;
|
||||
|
||||
// Re-initialize the color iterator if the iterator has previously reached the end of its index.
|
||||
if (!currentColor.hasNext())
|
||||
{
|
||||
this.currentColor = getColors().iterator();
|
||||
}
|
||||
|
||||
final Color color = currentColor.next();
|
||||
final Player player = (Player) getAssociatedPlayer();
|
||||
final Particle particle = getTrailType().getType();
|
||||
final Particle.DustOptions options = new Particle.DustOptions(color, 3F);
|
||||
final Location location = player.getLocation()
|
||||
.clone()
|
||||
.subtract(0, 1, 0);
|
||||
|
||||
location.getWorld()
|
||||
.spawnParticle(particle, location, 1, 0.0, 0.5, 0.0, options);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.Trail;
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class SimpleTrail implements Trail
|
||||
{
|
||||
private final UUID associatedPlayerUUID;
|
||||
private final TrailType trailType;
|
||||
|
||||
private Color staticColor = null;
|
||||
private Set<Color> gradientColor = null;
|
||||
private boolean active = false;
|
||||
|
||||
protected SimpleTrail(final Player player, final TrailType trailType)
|
||||
{
|
||||
this.associatedPlayerUUID = player.getUniqueId();
|
||||
this.trailType = trailType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getAssociatedPlayerUUID()
|
||||
{
|
||||
return associatedPlayerUUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OfflinePlayer getAssociatedPlayer()
|
||||
{
|
||||
return Bukkit.getOfflinePlayer(getAssociatedPlayerUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TrailType getTrailType()
|
||||
{
|
||||
return trailType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Color getColor()
|
||||
{
|
||||
return staticColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(@NotNull final Color color)
|
||||
{
|
||||
this.gradientColor = null;
|
||||
this.staticColor = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Set<Color> getColors()
|
||||
{
|
||||
return this.gradientColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColors(@NotNull final Set<Color> colors)
|
||||
{
|
||||
this.staticColor = null;
|
||||
this.gradientColor = colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGradient()
|
||||
{
|
||||
return gradientColor != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActive(final boolean active)
|
||||
{
|
||||
this.active = active;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import me.totalfreedom.particle.TrailType;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class StrobeTrail extends SimpleTrail
|
||||
{
|
||||
private final Particle.DustTransition dustTransition;
|
||||
|
||||
public StrobeTrail(final Player player, final Color from, final Color to)
|
||||
{
|
||||
super(player, TrailType.STROBE);
|
||||
this.dustTransition = new Particle.DustTransition(from, to, 3F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle()
|
||||
{
|
||||
if (!getAssociatedPlayer().isOnline() || !isActive()) return;
|
||||
|
||||
final Player player = (Player) getAssociatedPlayer();
|
||||
final Location location = player.getLocation()
|
||||
.clone()
|
||||
.subtract(0, 1, 0);
|
||||
location.getWorld()
|
||||
.spawnParticle(getTrailType().getType(), location, 1, 0.0, 0.5, 0.0, dustTransition);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package me.totalfreedom.fossil.trail.types;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class TrailProvider
|
||||
{
|
||||
public BasicTrail basicTrail(final Player player)
|
||||
{
|
||||
return new BasicTrail(player);
|
||||
}
|
||||
|
||||
}
|
@ -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 <T> The type of the context.
|
||||
* @see ContextProvider
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Context<T>
|
||||
{
|
||||
T get();
|
||||
|
||||
/**
|
||||
* Maps the context to another context.
|
||||
*
|
||||
* @param mapper The mapper function.
|
||||
* @param <S> The type of the mapped context.
|
||||
* @return The mapped context.
|
||||
*/
|
||||
default <S> Context<S> map(@NotNull final Function<T, S> 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<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link Double}.
|
||||
*/
|
||||
default @Nullable Double asDouble()
|
||||
{
|
||||
if (get() instanceof Double doub)
|
||||
@ -56,30 +89,51 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
default @Nullable Integer asInt() {
|
||||
if (get() instanceof Integer integer) {
|
||||
/**
|
||||
* @return The context as a {@link Integer}.
|
||||
*/
|
||||
default @Nullable Integer asInt()
|
||||
{
|
||||
if (get() instanceof Integer integer)
|
||||
{
|
||||
return integer;
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
default @Nullable Long asLong() {
|
||||
if (get() instanceof Long longg) {
|
||||
/**
|
||||
* @return The context as a {@link Byte}.
|
||||
*/
|
||||
default @Nullable Long asLong()
|
||||
{
|
||||
if (get() instanceof Long longg)
|
||||
{
|
||||
return longg;
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
default @Nullable Float asFloat() {
|
||||
if (get() instanceof Float floatt) {
|
||||
/**
|
||||
* @return The context as a {@link Float}.
|
||||
*/
|
||||
default @Nullable Float asFloat()
|
||||
{
|
||||
if (get() instanceof Float floatt)
|
||||
{
|
||||
return floatt;
|
||||
} else {
|
||||
} else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link Player}.
|
||||
*/
|
||||
default @Nullable Player asPlayer()
|
||||
{
|
||||
if (get() instanceof Player player)
|
||||
@ -91,6 +145,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link CommandSender}.
|
||||
*/
|
||||
default @Nullable CommandSender asCommandSender()
|
||||
{
|
||||
if (get() instanceof CommandSender commandSender)
|
||||
@ -102,11 +159,19 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
@ -118,6 +183,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link Location}.
|
||||
*/
|
||||
default @Nullable Location asLocation()
|
||||
{
|
||||
if (get() instanceof Location location)
|
||||
@ -129,6 +197,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link LivingEntity}.
|
||||
*/
|
||||
default @Nullable LivingEntity asLivingEntity()
|
||||
{
|
||||
if (get() instanceof LivingEntity livingEntity)
|
||||
@ -140,6 +211,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link Component}.
|
||||
*/
|
||||
default @Nullable Component asComponent()
|
||||
{
|
||||
if (get() instanceof Component component)
|
||||
@ -151,6 +225,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as a {@link Projectile}.
|
||||
*/
|
||||
default @Nullable Projectile asProjectile()
|
||||
{
|
||||
if (get() instanceof Projectile projectile)
|
||||
@ -162,6 +239,9 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The context as an {@link Action}.
|
||||
*/
|
||||
default @Nullable Action asAction()
|
||||
{
|
||||
if (get() instanceof Action action)
|
||||
@ -173,6 +253,22 @@ public interface Context<T>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context as a custom class. This will cast the object to the class if it is an instance of it.
|
||||
* <br>
|
||||
* Typically, Context objects are useful when used to collect unknown data and then cast it to a known type.
|
||||
* <br>
|
||||
* 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.
|
||||
* <p>
|
||||
* For example, if we have a Context<Object> and we already know that the wrapped object should be of type X,
|
||||
* we can use <code>X.class</code> 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 <U>
|
||||
* @return
|
||||
*/
|
||||
default <U> @Nullable U asCustom(Class<U> clazz)
|
||||
{
|
||||
if (clazz.isInstance(get()))
|
||||
|
@ -0,0 +1,21 @@
|
||||
package me.totalfreedom.api;
|
||||
|
||||
/**
|
||||
* Interpolates a range of values and returns the results in a {@link Double} array.
|
||||
* <br>
|
||||
* 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);
|
||||
}
|
@ -1,15 +1,27 @@
|
||||
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 <T> The type of object to serialize
|
||||
*/
|
||||
public interface Serializable<T>
|
||||
{
|
||||
/**
|
||||
* Serialize an object to a string.
|
||||
* Ideally, this should serialize to an SQL query for easy data transfer.
|
||||
* Serialize an object to a string. Ideally, this should serialize to an SQL query for easy data transfer.
|
||||
*
|
||||
* @param object The object to serialize
|
||||
* @return The serialized object
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@ -19,15 +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.
|
||||
* <p>
|
||||
* 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<Audience> 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();
|
||||
@ -40,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 */)
|
||||
@ -50,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)
|
||||
{
|
||||
@ -65,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.
|
||||
@ -118,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 <T> void sendTitlePart(@NotNull final TitlePart<T> 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));
|
||||
}
|
||||
}
|
||||
|
@ -2,46 +2,89 @@ package me.totalfreedom.base;
|
||||
|
||||
import me.totalfreedom.event.EventBus;
|
||||
import me.totalfreedom.service.FreedomExecutor;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable()
|
||||
public void onDisable()
|
||||
{
|
||||
getRegistrations().getServiceRegistry().register(this, eventBus);
|
||||
getExecutor().getSync()
|
||||
.execute(() -> getRegistrations()
|
||||
.getServiceRegistry()
|
||||
.startAll());
|
||||
Bukkit.getScheduler()
|
||||
.runTaskLater(this, () -> getRegistrations()
|
||||
.getServiceTaskRegistry()
|
||||
.stopAllServices(), 1L);
|
||||
|
||||
getRegistrations().getServiceTaskRegistry()
|
||||
.unregisterService(EventBus.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable()
|
||||
public void onEnable()
|
||||
{
|
||||
getRegistrations().getServiceRegistry().stopAll();
|
||||
getRegistrations().getServiceRegistry().unregister(EventBus.class, eventBus);
|
||||
}
|
||||
|
||||
public Registration getRegistrations()
|
||||
{
|
||||
return registration;
|
||||
getRegistrations().getServiceTaskRegistry()
|
||||
.registerService(SubscriptionProvider.asyncService(this, eventBus));
|
||||
getExecutor().getSync()
|
||||
.execute(() -> getRegistrations()
|
||||
.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;
|
||||
|
@ -1,64 +1,102 @@
|
||||
package me.totalfreedom.base;
|
||||
|
||||
import me.totalfreedom.data.GroupRegistry;
|
||||
import me.totalfreedom.data.BanRegistry;
|
||||
import me.totalfreedom.data.ConfigRegistry;
|
||||
import me.totalfreedom.data.ModuleRegistry;
|
||||
import me.totalfreedom.data.ServiceRegistry;
|
||||
import me.totalfreedom.data.UserRegistry;
|
||||
import me.totalfreedom.data.EventRegistry;
|
||||
import me.totalfreedom.data.GroupRegistry;
|
||||
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.
|
||||
* <br>
|
||||
* 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;
|
||||
private final ServiceRegistry serviceRegistry;
|
||||
/**
|
||||
* The {@link ServiceTaskRegistry}
|
||||
*/
|
||||
private final ServiceTaskRegistry serviceTaskRegistry;
|
||||
/**
|
||||
* The {@link ModuleRegistry}
|
||||
*/
|
||||
private final ModuleRegistry moduleRegistry;
|
||||
/**
|
||||
* The {@link GroupRegistry}
|
||||
*/
|
||||
private final GroupRegistry groupRegistry;
|
||||
private final BanRegistry banRegistry;
|
||||
/**
|
||||
* 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();
|
||||
this.serviceRegistry = new ServiceRegistry();
|
||||
this.serviceTaskRegistry = new ServiceTaskRegistry();
|
||||
this.moduleRegistry = new ModuleRegistry();
|
||||
this.groupRegistry = new GroupRegistry();
|
||||
this.banRegistry = new BanRegistry();
|
||||
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 ServiceRegistry getServiceRegistry()
|
||||
/**
|
||||
* @return The {@link ServiceTaskRegistry}
|
||||
*/
|
||||
public ServiceTaskRegistry getServiceTaskRegistry()
|
||||
{
|
||||
return serviceRegistry;
|
||||
return serviceTaskRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link GroupRegistry}
|
||||
*/
|
||||
public GroupRegistry getGroupRegistry()
|
||||
{
|
||||
return groupRegistry;
|
||||
}
|
||||
|
||||
public BanRegistry getBanRegistry()
|
||||
{
|
||||
return banRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link ConfigRegistry}
|
||||
*/
|
||||
public ConfigRegistry getConfigRegistry()
|
||||
{
|
||||
return configRegistry;
|
||||
|
@ -0,0 +1,202 @@
|
||||
package me.totalfreedom.command;
|
||||
|
||||
import me.totalfreedom.api.Context;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import me.totalfreedom.provider.ContextProvider;
|
||||
import me.totalfreedom.utils.logging.FreedomLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is acts as a delegate between our custom command implementation and the Bukkit API.
|
||||
* <br>
|
||||
* 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.
|
||||
* <span color=#ff0000>
|
||||
* <br>
|
||||
* This class is not thread-safe.
|
||||
* <br>
|
||||
* This class is not meant to be extended.
|
||||
* <br>
|
||||
* This class is not meant to be instantiated.
|
||||
* <br>
|
||||
* This class is not meant to be used outside Patchwork.
|
||||
* </span>
|
||||
*/
|
||||
public final class BukkitDelegate extends Command implements PluginIdentifiableCommand
|
||||
{
|
||||
private final JavaPlugin plugin;
|
||||
private final Commander command;
|
||||
private final boolean noConsole;
|
||||
|
||||
BukkitDelegate(final Commander command)
|
||||
{
|
||||
super(command.getInfo()
|
||||
.name());
|
||||
this.command = command;
|
||||
this.plugin = command.getPlugin();
|
||||
this.setDescription(command.getInfo()
|
||||
.description());
|
||||
this.setUsage(command.getInfo()
|
||||
.usage());
|
||||
this.setPermission(command.getPerms()
|
||||
.perm());
|
||||
this.setAliases(Arrays.asList(command.getInfo()
|
||||
.aliases()));
|
||||
this.permissionMessage(Component.text(command.getPerms()
|
||||
.noPerms()));
|
||||
this.noConsole = command.getPerms()
|
||||
.onlyPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull final CommandSender sender,
|
||||
@NotNull final String commandLabel,
|
||||
@NotNull final String[] args)
|
||||
{
|
||||
if (sender instanceof ConsoleCommandSender && noConsole)
|
||||
{
|
||||
sender.sendMessage(Component.text("This command can only be run by players."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getPermission() != null && !sender.hasPermission(getPermission()))
|
||||
{
|
||||
Component permissionMessage = permissionMessage();
|
||||
if (permissionMessage == null)
|
||||
permissionMessage = Component.text("You do not have permission to use this command.");
|
||||
sender.sendMessage(permissionMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0)
|
||||
{
|
||||
final ContextProvider provider = new ContextProvider();
|
||||
final Set<Subcommand> nodes = command.getSubcommands()
|
||||
.keySet();
|
||||
for (final Subcommand node : nodes)
|
||||
{
|
||||
processSubCommands(args, sender, provider, node);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command.getBaseMethod() != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
command.getBaseMethod()
|
||||
.invoke(command, sender);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
FreedomLogger.getLogger("Patchwork")
|
||||
.error(ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processSubCommands(final @NotNull String @NotNull [] args,
|
||||
final CommandSender sender, final ContextProvider provider,
|
||||
final Subcommand node)
|
||||
{
|
||||
final Class<?>[] argTypes = node.args();
|
||||
if (argTypes.length != args.length)
|
||||
return;
|
||||
|
||||
final Object[] objects = new Object[argTypes.length + 1];
|
||||
|
||||
for (int i = 0; i < argTypes.length; i++)
|
||||
{
|
||||
final Class<?> argType = argTypes[i];
|
||||
final String arg = args[i];
|
||||
|
||||
if (argType == String.class)
|
||||
continue;
|
||||
|
||||
final Context<?> context = () -> provider.fromString(arg, argType);
|
||||
objects[i] = context.get();
|
||||
}
|
||||
try
|
||||
{
|
||||
command.getSubcommands()
|
||||
.get(node)
|
||||
.invoke(command, sender, objects);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
FreedomLogger.getLogger("Patchwork")
|
||||
.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(final CommandSender sender, final String alias, final String[] args)
|
||||
{
|
||||
final Set<Completion> completions = command.getCompletions();
|
||||
final List<String> results = new ArrayList<>();
|
||||
for (final Completion completion : completions)
|
||||
{
|
||||
if (completion.index() != args.length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final String p : completion.args())
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case "%player%" -> results.addAll(Bukkit.getOnlinePlayers()
|
||||
.stream()
|
||||
.map(Player::getName)
|
||||
.toList());
|
||||
case "%world%" -> results.addAll(Bukkit.getWorlds()
|
||||
.stream()
|
||||
.map(World::getName)
|
||||
.toList());
|
||||
case "%number%" -> results.addAll(List.of(
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"));
|
||||
case "%location%" -> results.add("world,x,y,z");
|
||||
default -> results.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results.stream()
|
||||
.filter(s -> s.startsWith(args[args.length - 1]))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin getPlugin()
|
||||
{
|
||||
return this.plugin;
|
||||
}
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
package me.totalfreedom.command;
|
||||
|
||||
import me.totalfreedom.api.Context;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import me.totalfreedom.provider.ContextProvider;
|
||||
import me.totalfreedom.utils.FreedomLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class BukkitDelegator extends Command implements PluginIdentifiableCommand
|
||||
{
|
||||
private final JavaPlugin plugin;
|
||||
private final CommandBase command;
|
||||
private final boolean noConsole;
|
||||
|
||||
BukkitDelegator(final JavaPlugin plugin, final CommandBase command)
|
||||
{
|
||||
super(command.getInfo().name());
|
||||
this.command = command;
|
||||
this.plugin = command.getPlugin();
|
||||
this.setDescription(command.getInfo().description());
|
||||
this.setUsage(command.getInfo().usage());
|
||||
this.setPermission(command.getPerms().perm());
|
||||
this.setAliases(Arrays.asList(command.getInfo().aliases()));
|
||||
this.permissionMessage(Component.text(command.getPerms().noPerms()));
|
||||
this.noConsole = command.getPerms().onlyPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull final CommandSender sender,
|
||||
@NotNull final String commandLabel,
|
||||
@NotNull final String[] args)
|
||||
{
|
||||
if (commandLabel.isEmpty() || !commandLabel.equalsIgnoreCase(getName()))
|
||||
return false;
|
||||
|
||||
if (sender instanceof ConsoleCommandSender && noConsole)
|
||||
{
|
||||
sender.sendMessage(Component.text("This command can only be run by players."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getPermission() != null && !sender.hasPermission(getPermission()))
|
||||
{
|
||||
Component permissionMessage = permissionMessage();
|
||||
if (permissionMessage == null)
|
||||
permissionMessage = Component.text("You do not have permission to use this command.");
|
||||
sender.sendMessage(permissionMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0)
|
||||
{
|
||||
final ContextProvider provider = new ContextProvider();
|
||||
final Set<Subcommand> nodes = command.getSubcommands().keySet();
|
||||
for (final Subcommand node : nodes)
|
||||
{
|
||||
final Class<?>[] argTypes = node.args();
|
||||
if (argTypes.length != args.length)
|
||||
continue;
|
||||
|
||||
Object[] objects = new Object[0];
|
||||
|
||||
for (int i = 0; i < argTypes.length; i++)
|
||||
{
|
||||
final Class<?> argType = argTypes[i];
|
||||
final String arg = args[i];
|
||||
if (argType == String.class)
|
||||
continue;
|
||||
|
||||
final Context<?> context = () -> provider.fromString(arg);
|
||||
if (!argType.isInstance(context.get()))
|
||||
{
|
||||
throw new IllegalStateException(); // TODO: Change this.
|
||||
}
|
||||
objects = Arrays.copyOf(objects, objects.length + 1);
|
||||
objects[objects.length - 1] = context.get();
|
||||
}
|
||||
try
|
||||
{
|
||||
command.getSubcommands().get(node).invoke(command, objects);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
FreedomLogger.getLogger("Patchwork")
|
||||
.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command.getBaseMethodPair() != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
command.getBaseMethodPair().value().invoke(command, sender);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
FreedomLogger.getLogger("Patchwork")
|
||||
.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(final CommandSender sender, final String alias, final String[] args)
|
||||
{
|
||||
final Set<Completion> completions = command.getCompletions();
|
||||
final List<String> results = new ArrayList<>();
|
||||
for (final Completion completion : completions)
|
||||
{
|
||||
if (completion.index() != args.length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final String p : completion.args())
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case "%player%" -> results.addAll(Bukkit.getOnlinePlayers()
|
||||
.stream()
|
||||
.map(Player::getName)
|
||||
.toList());
|
||||
case "%world%" -> results.addAll(Bukkit.getWorlds()
|
||||
.stream()
|
||||
.map(World::getName)
|
||||
.toList());
|
||||
case "%number%" -> results.addAll(List.of(
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"));
|
||||
case "%location%" -> results.add("world,x,y,z");
|
||||
default -> results.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results.stream().filter(s -> s.startsWith(args[args.length - 1])).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Plugin getPlugin()
|
||||
{
|
||||
return this.plugin;
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package me.totalfreedom.command;
|
||||
|
||||
import me.totalfreedom.command.annotation.*;
|
||||
import me.totalfreedom.utils.Pair;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommandBase
|
||||
{
|
||||
private final JavaPlugin plugin;
|
||||
private final Info info;
|
||||
private final Permissive perms;
|
||||
private final Map<Subcommand, Method> subcommands;
|
||||
private final Set<Completion> completions;
|
||||
private final Pair<Base, Method> baseMethodPair;
|
||||
|
||||
protected CommandBase(final JavaPlugin plugin)
|
||||
{
|
||||
this.info = this.getClass().getDeclaredAnnotation(Info.class);
|
||||
this.perms = this.getClass().getDeclaredAnnotation(Permissive.class);
|
||||
this.plugin = plugin;
|
||||
this.subcommands = new HashMap<>();
|
||||
this.completions = new HashSet<>();
|
||||
|
||||
if (this.getClass().isAnnotationPresent(Base.class))
|
||||
{
|
||||
final Method method = Stream.of(this.getClass().getDeclaredMethods())
|
||||
.filter(m -> m.isAnnotationPresent(Base.class))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Base annotation present but no method found."));
|
||||
|
||||
this.baseMethodPair = new Pair<>(method.getDeclaredAnnotation(Base.class), method);
|
||||
} else
|
||||
{
|
||||
this.baseMethodPair = null;
|
||||
}
|
||||
|
||||
registerAnnotations();
|
||||
}
|
||||
|
||||
private void registerAnnotations()
|
||||
{
|
||||
Stream.of(this.getClass().getDeclaredMethods())
|
||||
.filter(method -> method.isAnnotationPresent(Subcommand.class))
|
||||
.forEach(method -> this.subcommands.put(
|
||||
method.getDeclaredAnnotation(Subcommand.class),
|
||||
method));
|
||||
|
||||
List.of(this.getClass().getDeclaredAnnotationsByType(Completion.class))
|
||||
.stream()
|
||||
.forEach(completions::add);
|
||||
}
|
||||
|
||||
public Pair<Base, Method> getBaseMethodPair()
|
||||
{
|
||||
return baseMethodPair;
|
||||
}
|
||||
|
||||
Info getInfo()
|
||||
{
|
||||
return this.info;
|
||||
}
|
||||
|
||||
Permissive getPerms()
|
||||
{
|
||||
return this.perms;
|
||||
}
|
||||
|
||||
public JavaPlugin getPlugin()
|
||||
{
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
Map<Subcommand, Method> getSubcommands()
|
||||
{
|
||||
return this.subcommands;
|
||||
}
|
||||
|
||||
Set<Completion> getCompletions()
|
||||
{
|
||||
return this.completions;
|
||||
}
|
||||
}
|
@ -1,25 +1,44 @@
|
||||
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.
|
||||
* <br>
|
||||
* 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;
|
||||
}
|
||||
|
||||
// TODO: Figure out how to use CommandExecutor and TabCompleter.
|
||||
// We need to find a way to resolve PluginCommands so we can
|
||||
// set the executor and tab completer as necessary.
|
||||
// OR we need to find an alternative way to process tab completions.
|
||||
public <T extends CommandBase> void registerCommand(final T command)
|
||||
/**
|
||||
* 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 <T> The type of the command.
|
||||
*/
|
||||
public <T extends Commander> void registerCommand(final T command)
|
||||
{
|
||||
final BukkitDelegator delegate = new BukkitDelegator(plugin, command);
|
||||
final BukkitDelegate delegate = new BukkitDelegate(command);
|
||||
|
||||
Bukkit.getCommandMap().register(plugin.getName(), delegate);
|
||||
Bukkit.getCommandMap()
|
||||
.register(plugin.getName(), delegate);
|
||||
}
|
||||
}
|
||||
|
186
Patchwork/src/main/java/me/totalfreedom/command/Commander.java
Normal file
186
Patchwork/src/main/java/me/totalfreedom/command/Commander.java
Normal file
@ -0,0 +1,186 @@
|
||||
package me.totalfreedom.command;
|
||||
|
||||
import me.totalfreedom.command.annotation.Base;
|
||||
import me.totalfreedom.command.annotation.Completion;
|
||||
import me.totalfreedom.command.annotation.Info;
|
||||
import me.totalfreedom.command.annotation.Permissive;
|
||||
import me.totalfreedom.command.annotation.Subcommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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<Subcommand, Method> subcommands;
|
||||
/**
|
||||
* A set of all {@link Completion} annotations for this command.
|
||||
*/
|
||||
private final Set<Completion> 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.
|
||||
* <p>
|
||||
* 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()
|
||||
.getDeclaredAnnotation(Info.class);
|
||||
this.perms = this.getClass()
|
||||
.getDeclaredAnnotation(Permissive.class);
|
||||
this.plugin = plugin;
|
||||
this.subcommands = new HashMap<>();
|
||||
this.completions = new HashSet<>();
|
||||
|
||||
if (this.getClass()
|
||||
.isAnnotationPresent(Base.class))
|
||||
{
|
||||
final Method method = Stream.of(this.getClass()
|
||||
.getDeclaredMethods())
|
||||
.filter(m -> m.isAnnotationPresent(Base.class))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException(
|
||||
"Base annotation present but no method found."));
|
||||
|
||||
this.baseMethod = method;
|
||||
} else
|
||||
{
|
||||
this.baseMethod = null;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
List.of(this.getClass()
|
||||
.getDeclaredAnnotationsByType(Completion.class))
|
||||
.stream()
|
||||
.forEach(completions::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the method which should be called when the command is executed without any arguments.
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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<Subcommand, Method> getSubcommands()
|
||||
{
|
||||
return this.subcommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A set of all {@link Completion} annotations for this command.
|
||||
*/
|
||||
@Nullable
|
||||
Set<Completion> getCompletions()
|
||||
{
|
||||
return this.completions;
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* This annotation is used to mark a method as the command's default method.
|
||||
* This is the method that will be run to execute the command when a user inputs /{command}
|
||||
* This annotation is used to mark a method as the command's default method. This is the method that will be run to
|
||||
* execute the command when a user inputs /{command}
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Base
|
||||
|
@ -1,15 +1,31 @@
|
||||
package me.totalfreedom.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
/**
|
||||
* Represents a tab completion for a command.
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package me.totalfreedom.command.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
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.
|
||||
* <br>
|
||||
* <u>This interface is <span color=#ff0000><b>NOT</b></span> intended for implementation and should
|
||||
* <span color=#ff0000><b>NOT</b></span> be used.</u>
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Completions
|
||||
{
|
||||
/**
|
||||
* @return The {@link Completion} annotations.
|
||||
*/
|
||||
Completion[] value();
|
||||
}
|
@ -3,14 +3,39 @@ 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 <b><u>must</u></b> 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 <u>"This is the default command description."</u>
|
||||
*
|
||||
* @return The command's description.
|
||||
*/
|
||||
String description() default "This is the default command description.";
|
||||
|
||||
/**
|
||||
* By default, this is set to <u>"/<command>"</u>
|
||||
*
|
||||
* @return The command's usage.
|
||||
*/
|
||||
String usage() default "/<command>";
|
||||
|
||||
/**
|
||||
* By default, this returns an empty array.
|
||||
*
|
||||
* @return The command's aliases.
|
||||
*/
|
||||
String[] aliases() default {};
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* Classes <u><b>MUST</b></u> 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 <u>false</u>.
|
||||
*
|
||||
* @return True if the command is only for players, false otherwise.
|
||||
*/
|
||||
boolean onlyPlayers() default false;
|
||||
|
||||
/**
|
||||
* By default, this is set to <u>"You do not have permission to use this command."</u>
|
||||
*
|
||||
* @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.";
|
||||
}
|
||||
|
@ -1,12 +1,40 @@
|
||||
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}.
|
||||
* <br>
|
||||
* 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.
|
||||
* <br>
|
||||
* 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 {};
|
||||
}
|
||||
|
@ -1,40 +1,132 @@
|
||||
package me.totalfreedom.config;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
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
|
||||
{
|
||||
YamlConfiguration asYaml();
|
||||
|
||||
/**
|
||||
* 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 <T> The type of the objects in the list.
|
||||
* @return The List object.
|
||||
*/
|
||||
<T> List<T> 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<String> 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 <T> The type of the value.
|
||||
*/
|
||||
<T> void set(String path, T value);
|
||||
|
||||
<T> T get(String path, Class<T> type);
|
||||
/**
|
||||
* Gets the value at the given path as the given type.
|
||||
* <p>
|
||||
* 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 <T> The type of the value.
|
||||
* @return The value at the given path.
|
||||
*/
|
||||
<T> T get(String path);
|
||||
|
||||
<T> T getOrDefault(String path, Class<T> type, T fallback);
|
||||
/**
|
||||
* Gets the value at the given path as the given type.
|
||||
* <p>
|
||||
* 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 <T> The type of the value.
|
||||
* @return The value at the given path.
|
||||
*/
|
||||
<T> T getOrDefault(String path, T fallback);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package me.totalfreedom.config;
|
||||
|
||||
public final class YamlWrapper
|
||||
{
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package me.totalfreedom.data;
|
||||
|
||||
import me.totalfreedom.security.ban.Ban;
|
||||
import me.totalfreedom.security.ban.BanID;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BanRegistry
|
||||
{
|
||||
private final List<Ban> bansList = new ArrayList<>();
|
||||
|
||||
public boolean addBan(final Ban ban) {
|
||||
return bansList.add(ban);
|
||||
}
|
||||
|
||||
public boolean removeBan(final Ban ban) {
|
||||
return bansList.remove(ban);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Ban getBan(final BanID banID)
|
||||
{
|
||||
for (final Ban ban : bansList)
|
||||
{
|
||||
if (ban.getBanID().matches(banID))
|
||||
{
|
||||
return ban;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -5,20 +5,43 @@ 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<String, Configuration> 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);
|
||||
|
@ -6,25 +6,51 @@ 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<FEvent> 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.
|
||||
* @param <T> The event type.
|
||||
* @return The event provider.
|
||||
*/
|
||||
public <T extends FEvent> EventProvider<T> getEvent(final Class<T> clazz)
|
||||
{
|
||||
for (final FEvent event : this.events)
|
||||
|
@ -1,40 +1,76 @@
|
||||
package me.totalfreedom.data;
|
||||
|
||||
import me.totalfreedom.security.perm.Group;
|
||||
import me.totalfreedom.security.Group;
|
||||
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<Group> groups;
|
||||
|
||||
/**
|
||||
* Creates a new group registry.
|
||||
*/
|
||||
public GroupRegistry()
|
||||
{
|
||||
this.groups = new ArrayList<>();
|
||||
}
|
||||
|
||||
public boolean registerGroup(final Group group) {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public boolean unregisterGroup(final Group 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);
|
||||
}
|
||||
|
||||
public Group getGroup(final String name) {
|
||||
/**
|
||||
* 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();
|
||||
for (final Group group : groups) {
|
||||
for (final Group group : groups)
|
||||
{
|
||||
final String n = s.serialize(group.getName());
|
||||
if (n.equalsIgnoreCase(name)) {
|
||||
if (n.equalsIgnoreCase(name))
|
||||
{
|
||||
return group;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
/**
|
||||
* @return The list of groups.
|
||||
*/
|
||||
public List<Group> getGroups()
|
||||
{
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
|
@ -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<JavaPlugin> 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,18 +38,30 @@ public class ModuleRegistry
|
||||
this.plugins.add(plugin);
|
||||
}
|
||||
|
||||
public void removeModule(final JavaPlugin 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 <T extends JavaPlugin> ModuleProvider<T> getModule(final Class<T> clazz)
|
||||
/**
|
||||
* Gets a module from the registry wrapped in a {@link ModuleProvider}.
|
||||
*
|
||||
* @param clazz The class of the module.
|
||||
* @param <T> The type of the module.
|
||||
* @return The module.
|
||||
*/
|
||||
public <T extends JavaPlugin> ModuleProvider<T> getProvider(final Class<T> clazz)
|
||||
{
|
||||
for (final JavaPlugin plugin : plugins)
|
||||
{
|
||||
if (clazz.isInstance(plugin))
|
||||
{
|
||||
return () -> (T) plugin;
|
||||
return () -> clazz.cast(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
package me.totalfreedom.data;
|
||||
|
||||
import me.totalfreedom.service.Service;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceRegistry
|
||||
{
|
||||
private final List<Service> services;
|
||||
|
||||
public ServiceRegistry()
|
||||
{
|
||||
this.services = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void startAll()
|
||||
{
|
||||
for (final Service service : this.services)
|
||||
{
|
||||
service.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopAll()
|
||||
{
|
||||
for (final Service service : this.services)
|
||||
{
|
||||
service.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// Suppressing is fine here; we know that the service is of type T extends Service,
|
||||
// and calling getClass() on this object would effectively be Class<T>, though we may lose
|
||||
// the identity of the code signature in the process.
|
||||
// In this case, that is fine.
|
||||
public <T extends Service> void register(final Plugin plugin, final T service)
|
||||
{
|
||||
this.services.add(service);
|
||||
if (!service.getClass().isInstance(service))
|
||||
{
|
||||
throw new UnknownError("""
|
||||
A critical issue has been encountered:
|
||||
The service %s is not an instance of itself.
|
||||
This is a critical issue and should be reported immediately.
|
||||
""".formatted(service.getClass().getName()));
|
||||
}
|
||||
Bukkit.getServicesManager().register(
|
||||
(Class<T>) service.getClass(),
|
||||
service,
|
||||
plugin,
|
||||
ServicePriority.Normal);
|
||||
}
|
||||
|
||||
public <T extends Service> RegisteredServiceProvider<T> getService(final Class<T> clazz)
|
||||
{
|
||||
return Bukkit.getServicesManager().getRegistration(clazz);
|
||||
}
|
||||
|
||||
public void unregister(final Class<? extends Service> clazz, final Service service)
|
||||
{
|
||||
this.services.remove(service);
|
||||
Bukkit.getServicesManager().unregister(clazz, service);
|
||||
}
|
||||
}
|
@ -0,0 +1,309 @@
|
||||
package me.totalfreedom.data;
|
||||
|
||||
import me.totalfreedom.service.Service;
|
||||
import me.totalfreedom.service.ServiceSubscription;
|
||||
import me.totalfreedom.service.SubscriptionProvider;
|
||||
import me.totalfreedom.service.Task;
|
||||
import me.totalfreedom.service.TaskSubscription;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A registry for all services and tasks registered with Patchwork.
|
||||
* <br>
|
||||
* This class is <b>not</b> thread-safe, and should only be accessed from the main server thread.
|
||||
* <br>
|
||||
* <br>
|
||||
* <b>Services</b> are tickable tasks which execute every single game tick. They are registered using
|
||||
* {@link #registerService(ServiceSubscription)} and can be started using {@link #startService(Class)}.
|
||||
* <br>
|
||||
* <br>
|
||||
* <b>Tasks</b> are runnable tasks which execute at the provided times in the {@link Task} and
|
||||
* {@link TaskSubscription} classes. These define whether the Task is repeating, delayed, or just a one-time task. Tasks
|
||||
* are registered using {@link #registerTask(TaskSubscription)} and can be started using {@link #startTask(Class)}.
|
||||
* <br>
|
||||
* <br>
|
||||
* <b>ServiceSubscriptions</b> and <b>TaskSubscriptions</b> can both be easily obtained using the
|
||||
* {@link SubscriptionProvider} utility class.
|
||||
*
|
||||
* @see Service
|
||||
* @see Task
|
||||
* @see ServiceSubscription
|
||||
* @see TaskSubscription
|
||||
* @see SubscriptionProvider
|
||||
*/
|
||||
public class ServiceTaskRegistry
|
||||
{
|
||||
/**
|
||||
* A list of all services registered with the registry.
|
||||
*/
|
||||
private final List<ServiceSubscription<?>> services;
|
||||
/**
|
||||
* A list of all tasks registered with the registry.
|
||||
*/
|
||||
private final List<TaskSubscription<?>> tasks;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the registry and initializes the service and task lists to new empty
|
||||
* {@link ArrayList}s.
|
||||
*/
|
||||
public ServiceTaskRegistry()
|
||||
{
|
||||
this.services = new ArrayList<>();
|
||||
this.tasks = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all services registered with the registry.
|
||||
* <br>
|
||||
* This method should be <i>avoided</i>, due to the fact that <b><i>modules may have registered their services after
|
||||
* this method has already been called.</i></b> In this case, it is preferred to start each service using
|
||||
* {@link #startService(Class)}.
|
||||
* <br>
|
||||
* However, <i><b>Patchwork calls this method when the server is starting up</b></i>, as Patchwork is the central
|
||||
* resource manager for registered tasks and services. Patchwork will call this method on the first server tick, so
|
||||
* unless you are registering services <b>AND</b> starting them <b>POST WORLD</b>, you do not need to worry about
|
||||
* starting your services.
|
||||
*/
|
||||
public void startAllServices()
|
||||
{
|
||||
for (final ServiceSubscription<?> service : this.services)
|
||||
{
|
||||
service.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all tasks registered with the registry.
|
||||
* <br>
|
||||
* This method should be <i>avoided</i>, due to the fact that <b><i>modules may have registered their tasks after
|
||||
* this method has already been called.</i></b> In this case, it is preferred to start each task using
|
||||
* {@link #startTask(Class)}.
|
||||
* <br>
|
||||
* However, <i><b>Patchwork calls this method when the server is starting up</b></i>, as Patchwork is the central
|
||||
* resource manager for registered tasks and services. Patchwork will call this method on the first server tick, so
|
||||
* unless you are registering tasks <b>AND</b> starting them <b>POST WORLD</b>, you do not need to worry about
|
||||
* starting your tasks.
|
||||
*/
|
||||
public void startAllTasks()
|
||||
{
|
||||
for (final TaskSubscription<?> task : this.tasks)
|
||||
{
|
||||
task.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all services registered with the registry.
|
||||
* <br>
|
||||
* This method should be <i>avoided</i>, due to the fact that <b><i>modules should be handling their own
|
||||
* registrations</i></b>. It is preferred to use {@link #stopService(Class)} for each service you would like to
|
||||
* stop.
|
||||
* <br>
|
||||
* However, <b><i>Patchwork calls this method when the server is shutting down</i></b>, or when Patchwork is being
|
||||
* disabled, as Patchwork is the central resource manager for registered tasks and services. Unless you are
|
||||
* <b>modifying service states while the server is running</b>, you do not need to worry about disabling or
|
||||
* unregistering your services.
|
||||
*/
|
||||
public void stopAllServices()
|
||||
{
|
||||
for (final ServiceSubscription<?> service : this.services)
|
||||
{
|
||||
service.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all tasks registered with the registry.
|
||||
* <br>
|
||||
* This method should be <i>avoided</i>, due to the fact that <b><i>modules should be handling their own
|
||||
* registrations</i></b>. It is preferred to use {@link #stopTask(Class)} for each task you would like to stop.
|
||||
* <br>
|
||||
* However, <b><i>Patchwork calls this method when the server is shutting down</i></b>, or when Patchwork is being
|
||||
* disabled, as Patchwork is the central resource manager for registered tasks and services. Unless you are
|
||||
* <b>modifying task states while the server is running</b>, you do not need to worry about disabling or
|
||||
* unregistering your tasks.
|
||||
*/
|
||||
public void stopAllTasks()
|
||||
{
|
||||
for (final TaskSubscription<?> task : this.tasks)
|
||||
{
|
||||
task.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a service with the registry.
|
||||
* <br>
|
||||
* <i>Services must be registered using <b>ServiceSubscriptions</b></i>, which can be easily obtained through the
|
||||
* {@link SubscriptionProvider} utility class.
|
||||
*
|
||||
* @param service The service you are trying to register.
|
||||
* @param <T> A generic type for type inference of the service being registered.
|
||||
*/
|
||||
public <T extends Service> void registerService(final ServiceSubscription<T> service)
|
||||
{
|
||||
this.services.add(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a task with the registry.
|
||||
* <br>
|
||||
* <i>Tasks must be registered using <b>TaskSubscriptions</b></i>, which can be easily obtained through the
|
||||
* {@link SubscriptionProvider} utility class.
|
||||
*
|
||||
* @param task The task you are trying to register.
|
||||
* @param <T> A generic type for type inference of the task being registered.
|
||||
*/
|
||||
public <T extends Task> void registerTask(final TaskSubscription<T> task)
|
||||
{
|
||||
this.tasks.add(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a service using the specified {@link Service} class.
|
||||
* <br>
|
||||
* <i>The service should already be registered with the registry as a <b>ServiceSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the service you are trying to start.
|
||||
* @see ServiceSubscription
|
||||
* @see #registerService(ServiceSubscription)
|
||||
*/
|
||||
public void startService(final Class<? extends Service> clazz)
|
||||
{
|
||||
this.getService(clazz)
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link ServiceSubscription} from the registry using the specified class.
|
||||
* <br>
|
||||
* <b>The class should be the <u>service class you are trying to locate</u>, not the class for the subscription
|
||||
* itself</b>.
|
||||
* <br>
|
||||
* <i>The service should have been registered previously as a <b>ServiceSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the service you are trying to locate.
|
||||
* @param <T> A generic type for type inference of the service requested.
|
||||
* @return The {@link ServiceSubscription} for the specified class, or null if it could not be found.
|
||||
* @see #registerService(ServiceSubscription)
|
||||
* @see ServiceSubscription
|
||||
*/
|
||||
@Nullable
|
||||
public <T extends Service> ServiceSubscription<T> getService(final Class<T> clazz)
|
||||
{
|
||||
for (final ServiceSubscription<?> service : this.services)
|
||||
{
|
||||
if (service.getService()
|
||||
.getClass()
|
||||
.equals(clazz))
|
||||
{
|
||||
return (ServiceSubscription<T>) service;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops a service using the specified {@link Service} class.
|
||||
* <br>
|
||||
* <i>The service should already be registered with the registry as a <b>ServiceSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the service you are trying to stop.
|
||||
* @see #registerService(ServiceSubscription)
|
||||
* @see ServiceSubscription
|
||||
*/
|
||||
public void stopService(final Class<? extends Service> clazz)
|
||||
{
|
||||
this.getService(clazz)
|
||||
.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a task using the specified {@link Task} class.
|
||||
* <br>
|
||||
* <i>The task should already be registered with the registry as a <b>TaskSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the task you are trying to start.
|
||||
* @see #registerTask(TaskSubscription)
|
||||
* @see TaskSubscription
|
||||
*/
|
||||
public void startTask(final Class<? extends Task> clazz)
|
||||
{
|
||||
this.getTask(clazz)
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link TaskSubscription} from the registry using the specified class.
|
||||
* <br>
|
||||
* <b>The class should be the <u>task class you are trying to locate</u>, not the class for the subscription
|
||||
* itself</b>.
|
||||
* <br>
|
||||
* <i>The task should have been registered previously as a <b>TaskSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the task you are trying to locate.
|
||||
* @param <T> A generic type for type inference of the task requested.
|
||||
* @return The {@link TaskSubscription} for the specified class, or null if it could not be found.
|
||||
* @see #registerTask(TaskSubscription)
|
||||
* @see TaskSubscription
|
||||
*/
|
||||
public <T extends Task> TaskSubscription<T> getTask(final Class<T> clazz)
|
||||
{
|
||||
for (final TaskSubscription<?> task : this.tasks)
|
||||
{
|
||||
if (task.getTask()
|
||||
.getClass()
|
||||
.equals(clazz))
|
||||
{
|
||||
return (TaskSubscription<T>) task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops a task using the specified {@link Task} class.
|
||||
* <br>
|
||||
* <i>The task should already be registered with the registry as a <b>TaskSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The class of the task you are trying to stop.
|
||||
* @see #registerTask(TaskSubscription)
|
||||
* @see TaskSubscription
|
||||
*/
|
||||
public void stopTask(final Class<? extends Task> clazz)
|
||||
{
|
||||
this.getTask(clazz)
|
||||
.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a service from the registry.
|
||||
* <br>
|
||||
* <i>The service should have been registered previously as a <b>ServiceSubscription</b></i>.
|
||||
*
|
||||
* @param service The service you are trying to unregister.
|
||||
* @see #registerService(ServiceSubscription)
|
||||
* @see ServiceSubscription
|
||||
*/
|
||||
public void unregisterService(final Class<? extends Service> clazz)
|
||||
{
|
||||
this.services.remove(getService(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a task from the registry.
|
||||
* <br>
|
||||
* <i>The task should have been registered previously as a <b>TaskSubscription</b></i>.
|
||||
*
|
||||
* @param clazz The task you are trying to unregister.
|
||||
* @see #registerTask(TaskSubscription)
|
||||
* @see TaskSubscription
|
||||
*/
|
||||
public void unregisterTask(final Class<? extends Task> clazz)
|
||||
{
|
||||
this.tasks.remove(getTask(clazz));
|
||||
}
|
||||
}
|
@ -2,34 +2,99 @@ package me.totalfreedom.data;
|
||||
|
||||
import me.totalfreedom.user.User;
|
||||
import me.totalfreedom.user.UserData;
|
||||
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<User, UserData> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()))
|
||||
.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<User, UserData> getUserDataMap()
|
||||
{
|
||||
return userDataMap;
|
||||
|
@ -0,0 +1,197 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
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<UUID, AbstractMenu> invByUUID = new HashMap<>();
|
||||
/**
|
||||
* A map of all open menus by the player's UUID.
|
||||
*/
|
||||
private static final Map<UUID, UUID> openInvs = new HashMap<>();
|
||||
private final Map<Integer, ClickAction> 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<>();
|
||||
this.displayable = new Displayable(size);
|
||||
this.displayableUUID = UUID.randomUUID();
|
||||
|
||||
invByUUID.put(getDisplayableUUID(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A map of all menus by their UUID.
|
||||
*/
|
||||
public static Map<UUID, AbstractMenu> getInvByUUID()
|
||||
{
|
||||
return invByUUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A map of all open menus by the player's UUID.
|
||||
*/
|
||||
public static Map<UUID, UUID> 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);
|
||||
if (action != null)
|
||||
{
|
||||
actionMap.put(slot, action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()
|
||||
.forEach(player ->
|
||||
{
|
||||
if (openInvs.get(player.getUniqueId())
|
||||
.equals(getDisplayableUUID()))
|
||||
{
|
||||
close(player);
|
||||
}
|
||||
});
|
||||
|
||||
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<Integer, ClickAction> 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 ItemStack item = new ItemStack(material, 1);
|
||||
final ItemMeta meta = item.getItemMeta();
|
||||
if (meta == null)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
meta.displayName(name);
|
||||
final List<Component> metaLore = Arrays.asList(lore);
|
||||
meta.lore(metaLore);
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
}
|
@ -0,0 +1,381 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.kyori.adventure.bossbar.BossBar;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
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();
|
||||
final float newProgress = currentProgress + (progress / 100.0F);
|
||||
|
||||
if (newProgress > 1) this.bossBar.progress(1.0F);
|
||||
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();
|
||||
final float newProgress = currentProgress - (progress / 100.0F);
|
||||
|
||||
if (newProgress < 0) this.bossBar.progress(0.0F);
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* A Builder class for {@link BossBar} objects.
|
||||
*/
|
||||
public static final class BossBarBuilder
|
||||
{
|
||||
/**
|
||||
* The flags that this Boss Bar will have.
|
||||
*/
|
||||
private final Set<BossBar.Flag> 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();
|
||||
this.color = BossBar.Color.GREEN;
|
||||
this.overlay = BossBar.Overlay.PROGRESS;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import me.totalfreedom.service.Task;
|
||||
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++;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
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);
|
||||
}
|
498
Patchwork/src/main/java/me/totalfreedom/display/Displayable.java
Normal file
498
Patchwork/src/main/java/me/totalfreedom/display/Displayable.java
Normal file
@ -0,0 +1,498 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid size for Displayable inventory");
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
this.contents = new ItemStack[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize()
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param size The new maximum stack size for items in this inventory.
|
||||
* @deprecated This method is not supported by Displayable inventories.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated(since = "1.19.4")
|
||||
public void setMaxStackSize(final int size)
|
||||
{
|
||||
// No implementation required
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(final int index)
|
||||
{
|
||||
if (index < 0 || index >= size)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return contents[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(final int index, final @Nullable ItemStack item)
|
||||
{
|
||||
if (index < 0 || index >= size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
contents[index] = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> addItem(final @NotNull ItemStack... items)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
final HashMap<Integer, ItemStack> remainingItems = new HashMap<>();
|
||||
|
||||
for (final ItemStack item : items)
|
||||
{
|
||||
int remainingAmount = item.getAmount();
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack current = contents[i];
|
||||
|
||||
if (current == null)
|
||||
{
|
||||
final int maxStackSize = item.getMaxStackSize();
|
||||
final int amountToAdd = Math.min(remainingAmount, maxStackSize);
|
||||
final ItemStack clone = item.clone();
|
||||
clone.setAmount(amountToAdd);
|
||||
contents[i] = clone;
|
||||
remainingAmount -= amountToAdd;
|
||||
|
||||
if (remainingAmount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingAmount > 0)
|
||||
{
|
||||
remainingItems.put(remainingItems.size(), new ItemStack(item.getType(), remainingAmount));
|
||||
}
|
||||
}
|
||||
|
||||
return remainingItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItem(final @NotNull ItemStack... items)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
final HashMap<Integer, ItemStack> removedItems = new HashMap<>();
|
||||
|
||||
for (final ItemStack item : items)
|
||||
{
|
||||
int remainingAmount = item.getAmount();
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack current = contents[i];
|
||||
|
||||
if (current != null && current.isSimilar(item))
|
||||
{
|
||||
final int amountToRemove = Math.min(remainingAmount, current.getAmount());
|
||||
current.setAmount(current.getAmount() - amountToRemove);
|
||||
remainingAmount -= amountToRemove;
|
||||
|
||||
if (current.getAmount() <= 0)
|
||||
{
|
||||
contents[i] = null;
|
||||
}
|
||||
|
||||
if (remainingAmount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingAmount < item.getAmount())
|
||||
{
|
||||
removedItems.put(removedItems.size(),
|
||||
new ItemStack(item.getType(), item.getAmount() - remainingAmount));
|
||||
}
|
||||
}
|
||||
|
||||
return removedItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItemAnySlot(final @NotNull ItemStack... items)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
return removeItem(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack @NotNull [] getContents()
|
||||
{
|
||||
return contents.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(final @Nullable ItemStack @NotNull [] items) throws IllegalArgumentException
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Items cannot be null");
|
||||
}
|
||||
if (items.length != size)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid size of items array");
|
||||
}
|
||||
System.arraycopy(items, 0, contents, 0, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack @NotNull [] getStorageContents()
|
||||
{
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(final @Nullable ItemStack @NotNull [] items) throws IllegalArgumentException
|
||||
{
|
||||
this.contents = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(final @NotNull Material material) throws IllegalArgumentException
|
||||
{
|
||||
for (final ItemStack item : contents)
|
||||
{
|
||||
if (item != null && item.getType() == material)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(final @Nullable ItemStack item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (final ItemStack content : contents)
|
||||
{
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(final @NotNull Material material, final int amount) throws IllegalArgumentException
|
||||
{
|
||||
int totalAmount = 0;
|
||||
for (final ItemStack item : contents)
|
||||
{
|
||||
if (item != null && item.getType() == material)
|
||||
{
|
||||
totalAmount += item.getAmount();
|
||||
if (totalAmount >= amount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(final @Nullable ItemStack item, final int amount)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int totalAmount = 0;
|
||||
for (final ItemStack content : contents)
|
||||
{
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
totalAmount += content.getAmount();
|
||||
if (totalAmount == amount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAtLeast(final @Nullable ItemStack item, final int amount)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int totalAmount = 0;
|
||||
for (final ItemStack content : contents)
|
||||
{
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
totalAmount += content.getAmount();
|
||||
if (totalAmount >= amount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ? extends ItemStack> all(final @NotNull Material material)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
final HashMap<Integer, ItemStack> matchingItems = new HashMap<>();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack item = contents[i];
|
||||
if (item != null && item.getType() == material)
|
||||
{
|
||||
matchingItems.put(i, item);
|
||||
}
|
||||
}
|
||||
return matchingItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ? extends ItemStack> all(final @Nullable ItemStack item)
|
||||
{
|
||||
final HashMap<Integer, ItemStack> matchingItems = new HashMap<>();
|
||||
if (item != null)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack content = contents[i];
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
matchingItems.put(i, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchingItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(final @NotNull Material material) throws IllegalArgumentException
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack item = contents[i];
|
||||
if (item != null && item.getType() == material)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(final @NotNull ItemStack item)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack content = contents[i];
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int firstEmpty()
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (contents[i] == null)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
for (final ItemStack content : contents)
|
||||
{
|
||||
if (content != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final @NotNull Material material) throws IllegalArgumentException
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack item = contents[i];
|
||||
if (item != null && item.getType() == material)
|
||||
{
|
||||
contents[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(final @NotNull ItemStack item)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final ItemStack content = contents[i];
|
||||
if (content != null && content.isSimilar(item))
|
||||
{
|
||||
contents[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(final int index)
|
||||
{
|
||||
if (index >= 0 && index < size)
|
||||
{
|
||||
contents[index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
contents[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int close()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<HumanEntity> getViewers()
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType()
|
||||
{
|
||||
return InventoryType.CHEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryHolder getHolder()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryHolder getHolder(final boolean useSnapshot)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator()
|
||||
{
|
||||
return iterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator(final int index)
|
||||
{
|
||||
return List.of(contents)
|
||||
.listIterator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Inventory getInventory()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import me.totalfreedom.utils.kyori.FreedomAdventure;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A view of a {@link Displayable} inventory.
|
||||
* <p>
|
||||
* 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;
|
||||
this.top = top;
|
||||
this.bottom = bottom;
|
||||
this.type = InventoryType.CHEST;
|
||||
this.title = FreedomAdventure.toPlainText(type.defaultTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Inventory getTopInventory()
|
||||
{
|
||||
return top;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Inventory getBottomInventory()
|
||||
{
|
||||
return bottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HumanEntity getPlayer()
|
||||
{
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(final @NotNull String title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getOriginalTitle()
|
||||
{
|
||||
return FreedomAdventure.toPlainText(type.defaultTitle());
|
||||
}
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
package me.totalfreedom.display;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>Empty main title</li>
|
||||
* <li>Empty subtitle</li>
|
||||
* <li>Default fade in time</li>
|
||||
* <li>Default fade out time</li>
|
||||
* <li>Default display duration</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see Title#DEFAULT_TIMES
|
||||
*/
|
||||
public TitleBuilder()
|
||||
{
|
||||
this.mainTitle = Component.empty();
|
||||
this.subTitle = Component.empty();
|
||||
this.fadeIn = Title.DEFAULT_TIMES.fadeIn();
|
||||
this.fadeOut = Title.DEFAULT_TIMES.fadeOut();
|
||||
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(
|
||||
this.mainTitle,
|
||||
this.subTitle,
|
||||
Title.Times.times(this.fadeIn, this.displayDuration, this.fadeOut)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,8 @@ package me.totalfreedom.economy;
|
||||
public interface EconomicEntity
|
||||
{
|
||||
/**
|
||||
* Gets the {@link EconomicEntityData} (which contains various common metadata about this {@link EconomicEntity}) associated with this class
|
||||
* Gets the {@link EconomicEntityData} (which contains various common metadata about this {@link EconomicEntity})
|
||||
* associated with this class
|
||||
*
|
||||
* @return the {@link EconomicEntityData}
|
||||
*/
|
||||
|
@ -15,6 +15,13 @@ public interface EconomicEntityData
|
||||
*/
|
||||
long getBalance();
|
||||
|
||||
/**
|
||||
* Sets the balance of the associated instance
|
||||
*
|
||||
* @param newBalance the new balance
|
||||
*/
|
||||
void setBalance(final long newBalance);
|
||||
|
||||
/**
|
||||
* Adds the provided amount to the associated instance's balance
|
||||
*
|
||||
@ -30,11 +37,4 @@ public interface EconomicEntityData
|
||||
* @return the new balance
|
||||
*/
|
||||
long removeFromBalance(final long amount);
|
||||
|
||||
/**
|
||||
* Sets the balance of the associated instance
|
||||
*
|
||||
* @param newBalance the new balance
|
||||
*/
|
||||
void setBalance(final long newBalance);
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ package me.totalfreedom.economy;
|
||||
/**
|
||||
* A transaction that can be changed.
|
||||
* <p>
|
||||
* IMPORTANT NOTE: Please ensure that all modifications of {@link MutableTransaction} happen BEFORE it is passed to a {@link Transactor} implementation
|
||||
* IMPORTANT NOTE: Please ensure that all modifications of {@link MutableTransaction} happen BEFORE it is passed to a
|
||||
* {@link Transactor} implementation
|
||||
*/
|
||||
public interface MutableTransaction extends Transaction
|
||||
{
|
||||
|
@ -27,7 +27,8 @@ public class EventBus extends Service
|
||||
public <T extends FEvent> T getEvent(final Class<T> eventClass)
|
||||
{
|
||||
final FEvent e = eventSet.stream()
|
||||
.filter(event -> event.getEventClass().equals(eventClass))
|
||||
.filter(event -> event.getEventClass()
|
||||
.equals(eventClass))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@ -37,7 +38,8 @@ public class EventBus extends Service
|
||||
public <T extends FEvent> EventSubscription<T> subscribe(final Class<T> eventClass, final Callback<T> callback)
|
||||
{
|
||||
final Context<T> eventContext = () -> eventSet.stream()
|
||||
.filter(event -> event.getEventClass().equals(eventClass))
|
||||
.filter(event -> event.getEventClass()
|
||||
.equals(eventClass))
|
||||
.findFirst()
|
||||
.map(eventClass::cast)
|
||||
.orElse(null);
|
||||
|
@ -7,24 +7,32 @@ class SubscriptionBox<T extends FEvent>
|
||||
{
|
||||
private final List<EventSubscription<T>> subscriptions;
|
||||
|
||||
public SubscriptionBox() {
|
||||
public SubscriptionBox()
|
||||
{
|
||||
this.subscriptions = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addSubscription(final EventSubscription<T> subscription) {
|
||||
public void addSubscription(final EventSubscription<T> subscription)
|
||||
{
|
||||
subscriptions.add(subscription);
|
||||
}
|
||||
|
||||
public void removeSubscription(final EventSubscription<?> subscription) {
|
||||
public void removeSubscription(final EventSubscription<?> subscription)
|
||||
{
|
||||
subscriptions.remove(subscription);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
subscriptions.forEach(s -> {
|
||||
if (!s.event().shouldCall()) return;
|
||||
public void tick()
|
||||
{
|
||||
subscriptions.forEach(s ->
|
||||
{
|
||||
if (!s.event()
|
||||
.shouldCall()) return;
|
||||
|
||||
s.callback().call(s.event());
|
||||
s.event().reset();
|
||||
s.callback()
|
||||
.call(s.event());
|
||||
s.event()
|
||||
.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
package me.totalfreedom.logging;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class BlockInteraction implements Interaction<BlockData>
|
||||
{
|
||||
private final Location location;
|
||||
private final UUID whoClicked;
|
||||
private final Instant when;
|
||||
private final BlockData originalState;
|
||||
private final BlockData newState;
|
||||
|
||||
public BlockInteraction(final Player player, final BlockData originalState, final BlockData newState)
|
||||
{
|
||||
this.location = player.getLocation();
|
||||
this.whoClicked = player.getUniqueId();
|
||||
this.when = Instant.now();
|
||||
this.originalState = originalState;
|
||||
this.newState = newState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getWhoClicked()
|
||||
{
|
||||
return whoClicked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getOriginalState()
|
||||
{
|
||||
return originalState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getNewState()
|
||||
{
|
||||
return newState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Instant getWhen()
|
||||
{
|
||||
return when;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Location getLocation()
|
||||
{
|
||||
return location;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package me.totalfreedom.logging;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ContainerInteraction implements Interaction<List<ItemStack>>
|
||||
{
|
||||
private final UUID whoClicked;
|
||||
private final List<ItemStack> originalState;
|
||||
private final List<ItemStack> newState;
|
||||
private final Instant when;
|
||||
private final Location location;
|
||||
|
||||
public ContainerInteraction(final Player player, final Container originalState, final Container newState)
|
||||
{
|
||||
this.whoClicked = player.getUniqueId();
|
||||
this.originalState = Collections.unmodifiableList(Arrays.asList(originalState.getInventory()
|
||||
.getContents()));
|
||||
this.newState = Collections.unmodifiableList(Arrays.asList(newState.getInventory()
|
||||
.getContents()));
|
||||
this.location = originalState.getLocation();
|
||||
this.when = Instant.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getWhoClicked()
|
||||
{
|
||||
return whoClicked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ItemStack> getOriginalState()
|
||||
{
|
||||
return originalState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ItemStack> getNewState()
|
||||
{
|
||||
return newState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Instant getWhen()
|
||||
{
|
||||
return when;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Location getLocation()
|
||||
{
|
||||
return location;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package me.totalfreedom.logging;
|
||||
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@Immutable
|
||||
public interface Interaction<T>
|
||||
{
|
||||
@NotNull
|
||||
static String format(@NotNull final Interaction<?> interaction)
|
||||
{
|
||||
return new InteractionFormatter().formatInteraction(interaction);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
UUID getWhoClicked();
|
||||
|
||||
@NotNull
|
||||
T getOriginalState();
|
||||
|
||||
@NotNull
|
||||
T getNewState();
|
||||
|
||||
@NotNull
|
||||
Instant getWhen();
|
||||
|
||||
@NotNull
|
||||
Location getLocation();
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
package me.totalfreedom.logging;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public final class InteractionFormatter
|
||||
{
|
||||
public String formatInteraction(final Interaction<?> interaction)
|
||||
{
|
||||
final String location = formatLocation(interaction.getLocation());
|
||||
final String world = formatWorld(interaction.getLocation()
|
||||
.getWorld());
|
||||
final String player = interaction.getWhoClicked()
|
||||
.toString();
|
||||
final String block = formatBlock(interaction.getLocation()
|
||||
.getBlock());
|
||||
final String when = formatTime(interaction.getWhen());
|
||||
|
||||
if (interaction instanceof ContainerInteraction container)
|
||||
{
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Container ")
|
||||
.append(block)
|
||||
.append(" at location ")
|
||||
.append(location)
|
||||
.append(" in world ")
|
||||
.append(world)
|
||||
.append(" was opened by ")
|
||||
.append(player)
|
||||
.append(" at ")
|
||||
.append(when)
|
||||
.append("\nHere is a list of items changed:\n");
|
||||
|
||||
container.getOriginalState()
|
||||
.stream()
|
||||
.filter(item ->
|
||||
{
|
||||
final ItemStack newItem = container.getNewState()
|
||||
.stream()
|
||||
.filter(item2 -> item2.isSimilar(item))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
return newItem == null || newItem.getAmount() != item.getAmount();
|
||||
})
|
||||
.forEach(item ->
|
||||
{
|
||||
final ItemStack newItem = container.getNewState()
|
||||
.stream()
|
||||
.filter(item2 -> item2.isSimilar(item))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
stringBuilder.append("Item ")
|
||||
.append(formatItemStack(item))
|
||||
.append(" was changed to ")
|
||||
.append(formatItemStack(newItem))
|
||||
.append("\n");
|
||||
});
|
||||
|
||||
stringBuilder.append(".");
|
||||
|
||||
return stringBuilder.toString();
|
||||
} else if (interaction instanceof BlockInteraction blockData)
|
||||
{
|
||||
final StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Block ")
|
||||
.append(block)
|
||||
.append(" at location ")
|
||||
.append(location)
|
||||
.append(" in world ")
|
||||
.append(world)
|
||||
.append(" was changed by ")
|
||||
.append(player)
|
||||
.append(" at ")
|
||||
.append(when)
|
||||
.append("\nBlock was changed from ")
|
||||
.append(blockData.getOriginalState()
|
||||
.getAsString())
|
||||
.append(" to ")
|
||||
.append(blockData.getNewState()
|
||||
.getAsString())
|
||||
.append(".");
|
||||
|
||||
return stringBuilder.toString();
|
||||
} else
|
||||
{
|
||||
throw new IllegalArgumentException("Unknown interaction type: " + interaction.getClass()
|
||||
.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Format: <x>,<y>,<z>
|
||||
public String formatLocation(final Location location)
|
||||
{
|
||||
return String.format("%s,%s,%s", location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
}
|
||||
|
||||
// Format: <world>
|
||||
public String formatWorld(final World world)
|
||||
{
|
||||
return world.getName();
|
||||
}
|
||||
|
||||
// Format: <material>
|
||||
public String formatBlock(final Block block)
|
||||
{
|
||||
return block.getType()
|
||||
.toString()
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
public String formatTime(final Instant instant)
|
||||
{
|
||||
final String trimmed = instant.toString()
|
||||
.replaceAll("[TZ]", " ");
|
||||
final int dotIndex = trimmed.indexOf('.');
|
||||
|
||||
return (dotIndex != -1)
|
||||
? trimmed.substring(0, dotIndex)
|
||||
: trimmed;
|
||||
}
|
||||
|
||||
// Format: <item>,<amount>
|
||||
public String formatItemStack(final ItemStack stack)
|
||||
{
|
||||
if (stack == null)
|
||||
{
|
||||
return String.format("%s,%s", "empty", "0");
|
||||
}
|
||||
|
||||
return String.format("%s,%s", stack.getType()
|
||||
.toString()
|
||||
.toLowerCase(), stack.getAmount());
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package me.totalfreedom.particle;
|
||||
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.World;
|
||||
|
||||
/**
|
||||
* A utility class for the 24 different note colors available in Minecraft. Each note is represented as a double value
|
||||
* between 0 and 1. Furthermore, each note is a multiple of 1/24 within that range of 0 to 1.
|
||||
* <p>
|
||||
* For example, the note G is represented as 1/24, or 0.042. The note C is represented as 6/24, or 0.25.
|
||||
* <p>
|
||||
* When spawning particles, the count must be set to 0 and the extra value set between 0 and 1. The extra value is the
|
||||
* size of the note particle. To add a color, use one of the provided methods in this class for the xOffset value in
|
||||
* {@link World#spawnParticle(Particle, double, double, double, int, double, double, double, double)}. The xOffset value
|
||||
* is the note color, with the yOffset and zOffset values set to 0.
|
||||
*/
|
||||
public final class NoteColorUtil
|
||||
{
|
||||
public static final double CYAN_NOTE_F_SHARP_LOW = 0;
|
||||
public static final double CYAN_NOTE_G = 0.042;
|
||||
public static final double GRAY_NOTE_G_SHARP = 0.083;
|
||||
public static final double GRAY_NOTE_A = 0.125;
|
||||
public static final double GRAY_NOTE_A_SHARP = 0.167;
|
||||
public static final double MAGENTA_NOTE_B = 0.208;
|
||||
public static final double RED_NOTE_C = 0.25;
|
||||
public static final double YELLOW_NOTE_C_SHARP = 0.292;
|
||||
public static final double YELLOW_NOTE_D = 0.333;
|
||||
public static final double YELLOW_NOTE_D_SHARP_LOW = 0.375;
|
||||
public static final double GRAY_NOTE_E = 0.417;
|
||||
public static final double GRAY_NOTE_F = 0.458;
|
||||
public static final double GRAY_NOTE_F_SHARP = 0.5;
|
||||
public static final double LIGHT_BLUE_NOTE_G = 0.542;
|
||||
public static final double BLUE_NOTE_G_SHARP = 0.583;
|
||||
public static final double PURPLE_NOTE_A = 0.625;
|
||||
public static final double PURPLE_NOTE_A_SHARP = 0.667;
|
||||
public static final double PURPLE_NOTE_B = 0.708;
|
||||
public static final double GRAY_NOTE_C = 0.75;
|
||||
public static final double GRAY_NOTE_C_SHARP = 0.792;
|
||||
public static final double GRAY_NOTE_D = 0.833;
|
||||
public static final double YELLOW_NOTE_D_SHARP_HIGH = 0.875;
|
||||
public static final double YELLOW_NOTE_E = 0.917;
|
||||
public static final double YELLOW_NOTE_F = 0.958;
|
||||
public static final double CYAN_NOTE_F_SHARP_HIGH = 1;
|
||||
public static final double BLACK_NOTE_NA = 32768;
|
||||
|
||||
private NoteColorUtil()
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
@ -1,11 +1,124 @@
|
||||
package me.totalfreedom.particle;
|
||||
|
||||
import me.totalfreedom.api.Interpolator;
|
||||
import me.totalfreedom.utils.InterpolationUtils;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Particle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a Trail instance for a specific player.
|
||||
*/
|
||||
public interface Trail
|
||||
{
|
||||
/**
|
||||
* Returns the UUID of the player associated with the trail. This is for usage with our persistant storage container
|
||||
* so that we can safely send and retrieve the trails without having to directly reference a player object.
|
||||
* <br>
|
||||
* TL;DR Memory optimization!
|
||||
*
|
||||
* @return The UUID of the player associated with this trail.
|
||||
*/
|
||||
@NotNull
|
||||
UUID getAssociatedPlayerUUID();
|
||||
|
||||
/**
|
||||
* Returns the player associated with this trail. Trails are user specific, and should be persistent across all
|
||||
* usages. This is also used when displaying the particles, as they will be relative to the player's back, which is
|
||||
* an inverse offset of the player's eye location. We use OfflinePlayer as we can make a simple check and cast to
|
||||
* determine if the player is online when spawning trails.
|
||||
*
|
||||
* @return The player associated with this Trail.
|
||||
*/
|
||||
@NotNull
|
||||
OfflinePlayer getAssociatedPlayer();
|
||||
|
||||
/**
|
||||
* Gets the Trail Type of this trail. This is used to determine what type of trail this is, and what
|
||||
* {@link Particle} it should use.
|
||||
*
|
||||
* @return The Trail Type of this trail.
|
||||
* @see TrailType
|
||||
*/
|
||||
@NotNull
|
||||
TrailType getTrailType();
|
||||
|
||||
@Nullable Color getColor();
|
||||
/**
|
||||
* This method is nullable because if the value of {@link #isGradient()} is true, then {@link #getColors()} should
|
||||
* be used instead, as that will contain the color data for our trail.
|
||||
* <br>
|
||||
* However, this method will also be null if the particle type is not colorable.
|
||||
*
|
||||
* @return The color of the trail, or null if the trail is a gradient or non-colorable.
|
||||
* @see Particle
|
||||
* @see #getColors();
|
||||
*/
|
||||
@Nullable
|
||||
Color getColor();
|
||||
|
||||
/**
|
||||
* Sets the static color of the trail. If you are trying to use a gradient, use {@link #setColors(Set)} instead.
|
||||
* <br>
|
||||
*
|
||||
* @param color The color to set the trail to.
|
||||
*/
|
||||
void setColor(@NotNull Color color);
|
||||
|
||||
/**
|
||||
* This method is nullable because if the value of {@link #isGradient()} is false, then {@link #getColor()} should
|
||||
* be used instead, as our trail is a single static color.
|
||||
* <br>
|
||||
* However, this method will also be null if the particle type is not colorable.
|
||||
*
|
||||
* @return The colors of the trail, or null if the trail is not a gradient or non-colorable.
|
||||
* @see #getColor()
|
||||
* @see Particle
|
||||
* @see InterpolationUtils
|
||||
* @see Interpolator
|
||||
*/
|
||||
@Nullable
|
||||
Set<Color> getColors();
|
||||
|
||||
/**
|
||||
* Sets the colors of the trail. If you are trying to use a static color, use {@link #setColor(Color)} instead.
|
||||
* <br>
|
||||
* This should be used for trails that iterate over a set of colors, such as a rainbow trail.
|
||||
*
|
||||
* @param colors The colors to set the trail to. It is recommended to use {@link InterpolationUtils} to generate
|
||||
* interpolated gradients for this.
|
||||
*/
|
||||
void setColors(@NotNull Set<Color> colors);
|
||||
|
||||
/**
|
||||
* Validates whether this Trail is a gradient or a static trail.
|
||||
* <br>
|
||||
* This is entirely based on whether {@link #getColors()} returns null or not.
|
||||
*
|
||||
* @return True if {@link #getColors()} is not null, false otherwise.
|
||||
*/
|
||||
boolean isGradient();
|
||||
|
||||
/**
|
||||
* Gets whether the trail is active.
|
||||
*
|
||||
* @return True if the trail is active, false if it is not.
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Turn the trail on or off.
|
||||
*
|
||||
* @param active True if the trail should be active, false if it should not.
|
||||
*/
|
||||
void setActive(final boolean active);
|
||||
|
||||
/**
|
||||
* Spawns a particle (if gradient, the next particle) on the supplied location object.
|
||||
*/
|
||||
void spawnParticle();
|
||||
}
|
||||
|
@ -4,10 +4,30 @@ import org.bukkit.Particle;
|
||||
|
||||
public enum TrailType
|
||||
{
|
||||
/**
|
||||
* Default trail type. Uses {@link Particle#REDSTONE}. This trail is colorable. Use {@link Particle.DustOptions} to
|
||||
* set the particle properties.
|
||||
*/
|
||||
DEFAULT(Particle.REDSTONE),
|
||||
/**
|
||||
* A trail that uses {@link Particle#HEART}. This is not modifiable and will always have the same size shape and
|
||||
* color.
|
||||
*/
|
||||
HEART(Particle.HEART),
|
||||
/**
|
||||
* A trail that uses {@link Particle#FLAME}. This is not modifiable and will always have the same size shape and
|
||||
* color.
|
||||
*/
|
||||
FLAME(Particle.FLAME),
|
||||
/**
|
||||
* A trail that uses {@link Particle#REDSTONE}. This particle however is rainbow-colored by default and cannot have
|
||||
* additional options set.
|
||||
*/
|
||||
RAINBOW(Particle.REDSTONE),
|
||||
/**
|
||||
* A trail that uses {@link Particle#NOTE}. This is colorable, however you are limited to the 24 different note
|
||||
* colors available in Minecraft.
|
||||
*/
|
||||
MUSIC(Particle.NOTE),
|
||||
SNOW(Particle.SNOWBALL),
|
||||
SPELL(Particle.SPELL_MOB),
|
||||
|
@ -3,6 +3,7 @@ package me.totalfreedom.provider;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -14,13 +15,14 @@ import java.util.stream.Stream;
|
||||
|
||||
public class ContextProvider
|
||||
{
|
||||
public Object fromString(final String string)
|
||||
public <T> T fromString(final String string, final Class<T> clazz)
|
||||
{
|
||||
return Stream.of(toBoolean(string),
|
||||
toDouble(string),
|
||||
toInt(string),
|
||||
toLong(string),
|
||||
toFloat(string),
|
||||
toMaterial(string),
|
||||
toPlayer(string),
|
||||
toWorld(string),
|
||||
toLocation(string),
|
||||
@ -28,26 +30,26 @@ public class ContextProvider
|
||||
toComponent(string))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(string);
|
||||
.map(clazz::cast)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private @Nullable Boolean toBoolean(final String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Boolean.parseBoolean(string);
|
||||
} catch (Exception e)
|
||||
{
|
||||
// Previously we used Boolean#parseBoolean, but that will always return a value and does not throw
|
||||
// an exception. This means that if the string is not "true" or "false", it will return false.
|
||||
if (string.equalsIgnoreCase("true")) return true;
|
||||
if (string.equalsIgnoreCase("false")) return false;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Double toDouble(final String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Double.parseDouble(string);
|
||||
} catch (Exception e)
|
||||
} catch (NumberFormatException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@ -58,7 +60,7 @@ public class ContextProvider
|
||||
try
|
||||
{
|
||||
return Integer.parseInt(string);
|
||||
} catch (Exception e)
|
||||
} catch (NumberFormatException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@ -69,7 +71,7 @@ public class ContextProvider
|
||||
try
|
||||
{
|
||||
return Long.parseLong(string);
|
||||
} catch (Exception e)
|
||||
} catch (NumberFormatException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@ -80,31 +82,27 @@ public class ContextProvider
|
||||
try
|
||||
{
|
||||
return Float.parseFloat(string);
|
||||
} catch (Exception e)
|
||||
} catch (NumberFormatException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Material toMaterial(final String string)
|
||||
{
|
||||
return Material.matchMaterial(string);
|
||||
}
|
||||
|
||||
private @Nullable Player toPlayer(final String string)
|
||||
{
|
||||
return Bukkit.getPlayer(string);
|
||||
}
|
||||
|
||||
private @Nullable CommandSender toCommandSender(final String string)
|
||||
{
|
||||
if (toPlayer(string) == null) return null;
|
||||
|
||||
return toPlayer(string);
|
||||
}
|
||||
|
||||
private @Nullable World toWorld(final String string)
|
||||
{
|
||||
return Bukkit.getWorld(string);
|
||||
}
|
||||
|
||||
// If we decide to, we can "modify" this to use spaces
|
||||
// and adjust our inputs accordingly.
|
||||
/**
|
||||
* When using this method, the input string must be formatted as
|
||||
* <br>
|
||||
@ -127,6 +125,13 @@ public class ContextProvider
|
||||
return new Location(toWorld(split[0]), x, y, z);
|
||||
}
|
||||
|
||||
private @Nullable CommandSender toCommandSender(final String string)
|
||||
{
|
||||
if (toPlayer(string) == null) return null;
|
||||
|
||||
return toPlayer(string);
|
||||
}
|
||||
|
||||
private @NotNull Component toComponent(final String string)
|
||||
{
|
||||
return Component.text(string);
|
||||
|
41
Patchwork/src/main/java/me/totalfreedom/security/Group.java
Normal file
41
Patchwork/src/main/java/me/totalfreedom/security/Group.java
Normal file
@ -0,0 +1,41 @@
|
||||
package me.totalfreedom.security;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
/**
|
||||
* Represents a permissible group which holds a set of permissions that can then be applied to a User / Player.
|
||||
*/
|
||||
public interface Group extends PermissionHolder
|
||||
{
|
||||
/**
|
||||
* @return The name of the group.
|
||||
*/
|
||||
Component getName();
|
||||
|
||||
/**
|
||||
* @return The prefix of the group.
|
||||
*/
|
||||
Component getPrefix();
|
||||
|
||||
/**
|
||||
* @return The abbreviation of the group.
|
||||
*/
|
||||
Component getAbbreviation();
|
||||
|
||||
/**
|
||||
* @return The weight of the group.
|
||||
*/
|
||||
int getWeight();
|
||||
|
||||
/**
|
||||
* If more than one group is marked as default, the first retrieved default group will be used.
|
||||
*
|
||||
* @return Whether this is the default group.
|
||||
*/
|
||||
boolean isDefault();
|
||||
|
||||
/**
|
||||
* @return Whether the group is hidden.
|
||||
*/
|
||||
boolean isHidden();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package me.totalfreedom.security.perm;
|
||||
package me.totalfreedom.security;
|
||||
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
@ -21,11 +21,7 @@ public interface Node
|
||||
|
||||
boolean isExpired();
|
||||
|
||||
boolean isPermanent();
|
||||
|
||||
boolean isTemporary();
|
||||
|
||||
boolean wildcard();
|
||||
|
||||
boolean negated();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package me.totalfreedom.security.perm;
|
||||
package me.totalfreedom.security;
|
||||
|
||||
public interface NodeBuilder
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
package me.totalfreedom.security.perm;
|
||||
package me.totalfreedom.security;
|
||||
|
||||
public enum NodeType
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
package me.totalfreedom.security.perm;
|
||||
package me.totalfreedom.security;
|
||||
|
||||
import org.bukkit.permissions.Permissible;
|
||||
|
@ -1,78 +0,0 @@
|
||||
package me.totalfreedom.security.ban;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a physical ban entry. This is used to store information about a ban,
|
||||
* such as the player who was banned, the reason for why they were banned, the individual who issued the ban,
|
||||
* when the ban expires, and when the ban was created.
|
||||
* <br>
|
||||
* Ban information is stored in the Database with the {@link BanID} as the PRIMARY KEY.
|
||||
*/
|
||||
public interface Ban
|
||||
{
|
||||
/**
|
||||
* Gets the ID of this ban. This is an object which represents a string prefixed with either a T or a P,
|
||||
* and suffixed with a 6-10 digit numerical code. This is used to identify the ban in the database, and for
|
||||
* easier ban referencing.
|
||||
*
|
||||
* @return The ID of this ban.
|
||||
*/
|
||||
BanID getBanID();
|
||||
|
||||
/**
|
||||
* Gets the UUID of the player who was banned. This is formatted as a UUID
|
||||
* which allows us to retrieve the particular player instance, if applicable, and also
|
||||
* have a true identifier to check against user logins.
|
||||
*
|
||||
* @return The UUID of the player who was banned.
|
||||
*/
|
||||
UUID getOffenderID();
|
||||
|
||||
/**
|
||||
* Gets the reason that the player was banned for. Typically, the default reason is "You are banned!".
|
||||
* We've forced implementations to require a reason, as it's important to know why a player was banned.
|
||||
*
|
||||
* @return The reason that the player was banned for.
|
||||
*/
|
||||
String getReason();
|
||||
|
||||
/**
|
||||
* Gets the username of the individual who issued the ban. This is not a reliable way to store data, but
|
||||
* in our case, we should not need to interact with the ban issuer from the code. This is simply for
|
||||
* reference purposes.
|
||||
*
|
||||
* @return The username of the individual who issued the ban.
|
||||
*/
|
||||
String getBanIssuer();
|
||||
|
||||
/**
|
||||
* Gets the {@link Instant} which this ban was created.
|
||||
*
|
||||
* @return The ban's creation time.
|
||||
*/
|
||||
Instant getCreationTime();
|
||||
|
||||
/**
|
||||
* Gets the {@link Instant} which this ban is due to expire, if applicable.
|
||||
* This method is annotated as {@link Nullable}, as permanent bans do not have an expiry date.
|
||||
*
|
||||
* @return The ban's expiry time, or null if the ban is permanent.
|
||||
*/
|
||||
@Nullable
|
||||
Instant getExpiry();
|
||||
|
||||
/**
|
||||
* Checks if the ban has expired. This will return false if:
|
||||
* <ul>
|
||||
* <li>The {@link Instant} returned by {@link #getExpiry()} is null.</li>
|
||||
* <li>The {@link Instant} returned by {@link #getExpiry()} is after the current time.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return True if the ban has expired, false otherwise.
|
||||
*/
|
||||
boolean isExpired();
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package me.totalfreedom.security.ban;
|
||||
|
||||
/**
|
||||
* Represents an ID for a ban. These are formatted either as:
|
||||
* <p>
|
||||
* P-00129381
|
||||
* <br>
|
||||
* T-00128381
|
||||
* <br>
|
||||
* </p>
|
||||
* Where P marks a ban as permanent, and T marks a ban as temporary.
|
||||
*/
|
||||
public interface BanID
|
||||
{
|
||||
/**
|
||||
* This method returns the full Ban ID.
|
||||
*
|
||||
* @return The actual ID.
|
||||
*/
|
||||
String getID();
|
||||
|
||||
/**
|
||||
* This method returns the ban type denominator character for the Ban ID.
|
||||
* This would either be T or P, where T = temporary and P = permanent.
|
||||
*
|
||||
* @return The ban type denominator character for the Ban ID.
|
||||
*/
|
||||
char getIDPrefix();
|
||||
|
||||
/**
|
||||
* Gets the numerical tag of this ban ID.
|
||||
* This would be the numerical section of the full Ban ID.
|
||||
*
|
||||
* @return The numerical tag of this ban ID.
|
||||
*/
|
||||
int getNumericalTag();
|
||||
|
||||
/**
|
||||
* Checks the prefix of the Ban ID to see whether if it is permanent.
|
||||
*
|
||||
* @return true if the Ban ID is prefixed with a P, false otherwise.
|
||||
*/
|
||||
boolean isPermanent();
|
||||
|
||||
default boolean matches(BanID other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
return (getIDPrefix() == other.getIDPrefix())
|
||||
&& (getNumericalTag() == other.getNumericalTag());
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package me.totalfreedom.security.perm;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public interface Group extends PermissionHolder
|
||||
{
|
||||
Component getName();
|
||||
|
||||
Component getPrefix();
|
||||
|
||||
Component getAbbreviation();
|
||||
|
||||
int getWeight();
|
||||
|
||||
boolean isDefault();
|
||||
|
||||
boolean isHidden();
|
||||
}
|
@ -2,58 +2,141 @@ package me.totalfreedom.service;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* This is a holder class for {@link Executor} objects that are used to delegate runnable tasks to the Bukkit Scheduler.
|
||||
* This class is here for both convenience purposes, and also for the sake of providing easy access to executors for
|
||||
* {@link CompletableFuture} invocations.
|
||||
*/
|
||||
public class FreedomExecutor
|
||||
{
|
||||
/**
|
||||
* An executor which runs tasks synchronously.
|
||||
*/
|
||||
private final Executor syncExecutor;
|
||||
/**
|
||||
* An executor which runs tasks asynchronously.
|
||||
*/
|
||||
private final Executor asyncExecutor;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FreedomExecutor} instance.
|
||||
*/
|
||||
public FreedomExecutor()
|
||||
{
|
||||
syncExecutor = r -> Bukkit.getScheduler().runTask(CommonsBase.getInstance(), r);
|
||||
asyncExecutor = r -> Bukkit.getScheduler().runTaskAsynchronously(CommonsBase.getInstance(), r);
|
||||
syncExecutor = r -> Bukkit.getScheduler()
|
||||
.runTask(CommonsBase.getInstance(), r);
|
||||
asyncExecutor = r -> Bukkit.getScheduler()
|
||||
.runTaskAsynchronously(CommonsBase.getInstance(), r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, synchronously.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor singleExecutor(final JavaPlugin plugin)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTask(plugin, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, synchronously. This
|
||||
* Executor will wait for the supplied delay before executing the runnable.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @param delay The delay to wait before executing the runnable.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor delayedExecutor(final JavaPlugin plugin, final long delay)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskLater(plugin, r, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} tthat is capable of executing a runnable on a periodic basis, synchronously. This
|
||||
* executor can also be supplied a delay to indicate it should wait the specified amount of time before executing
|
||||
* the runnable for the first time.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @param initialDelay The delay to wait before executing the runnable for the first time.
|
||||
* @param period The period to wait between each execution of the runnable.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor periodicExecutor(final JavaPlugin plugin, final long initialDelay, final long period)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskTimer(plugin, r, initialDelay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, asynchronously.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor asynchronousSingleExecutor(final JavaPlugin plugin)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskAsynchronously(plugin, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} that is capable of executing a runnable one singular time, asynchronously. This
|
||||
* Executor will wait for the supplied delay before executing the runnable.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @param delay The delay to wait before executing the runnable.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor asynchronousDelayedExecutor(final JavaPlugin plugin, final long delay)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskLaterAsynchronously(plugin, r, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Executor} tthat is capable of executing a runnable on a periodic basis, asynchronously. This
|
||||
* executor can also be supplied a delay to indicate it should wait the specified amount of time before executing
|
||||
* the runnable for the first time.
|
||||
*
|
||||
* @param plugin The plugin to run the task for.
|
||||
* @param delay The delay to wait before executing the runnable for the first time.
|
||||
* @param period The period to wait between each execution of the runnable.
|
||||
* @return A new {@link Executor} instance.
|
||||
*/
|
||||
public Executor asynchronousPeriodicExecutor(final JavaPlugin plugin, final long delay, final long period)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskTimerAsynchronously(plugin, r, delay, period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the synchronous executor instance. This is a convenience for {@link CompletableFuture} invocations, when
|
||||
* defining a custom executor for the {@link CompletableFuture}.
|
||||
*
|
||||
* @return The synchronous executor instance.
|
||||
*/
|
||||
public Executor getSync()
|
||||
{
|
||||
return syncExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the asynchronous executor instance. This is a convenience for {@link CompletableFuture} invocations, when
|
||||
* defining a custom executor for the {@link CompletableFuture}.
|
||||
*
|
||||
* @return The asynchronous executor instance.
|
||||
*/
|
||||
public Executor getAsync()
|
||||
{
|
||||
return asyncExecutor;
|
||||
}
|
||||
|
||||
public Executor scheduled(final long delay, final long period)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskTimer(
|
||||
CommonsBase.getInstance(),
|
||||
r,
|
||||
delay,
|
||||
period);
|
||||
}
|
||||
|
||||
public Executor scheduledAsync(final long delay, final long period)
|
||||
{
|
||||
return r -> Bukkit.getScheduler()
|
||||
.runTaskTimerAsynchronously(
|
||||
CommonsBase.getInstance(),
|
||||
r,
|
||||
delay,
|
||||
period);
|
||||
}
|
||||
|
||||
public void runSync(@NotNull final Task task)
|
||||
{
|
||||
getSync().execute(task);
|
||||
}
|
||||
|
||||
public void runAsync(@NotNull final Task task)
|
||||
{
|
||||
getAsync().execute(task);
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
package me.totalfreedom.service;
|
||||
|
||||
import me.totalfreedom.base.CommonsBase;
|
||||
|
||||
/**
|
||||
* Represents a ticking service. Services may be asynchronous or synchronous, however there are some restrictions:
|
||||
* <ul>
|
||||
* <li>Sync services may not have a complexity greater than 5.</li>
|
||||
* <li>Async services may not interact with the Bukkit API in any form.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public abstract class Service
|
||||
{
|
||||
/**
|
||||
* The name of the service.
|
||||
*/
|
||||
private final String name;
|
||||
private boolean isActive = false;
|
||||
|
||||
/**
|
||||
* Creates a new service with the given name.
|
||||
*
|
||||
* @param name The name of the service.
|
||||
*/
|
||||
protected Service(final String name)
|
||||
{
|
||||
this.name = name;
|
||||
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
isActive = true;
|
||||
CommonsBase.getInstance()
|
||||
.getExecutor()
|
||||
.getSync()
|
||||
.execute(() ->
|
||||
{
|
||||
while (isActive)
|
||||
{
|
||||
tick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called every single tick, or at least, every tick interval described by the holding
|
||||
* {@link ServiceSubscription}. Since this runs every single tick, the body of this method should not have a
|
||||
* complexity higher than 5. This includes Cyclomatic, Cognitive, and NPath complexities. If the service is
|
||||
* asynchronous, there is a bit more flexibility with the complexity rating, extending to no more than 10. However,
|
||||
* it's generally good practice to keep the complexity of ticking services as low as possible to avoid extensive
|
||||
* resource consumption.
|
||||
*
|
||||
* @see ServiceSubscription
|
||||
* @see SubscriptionProvider
|
||||
*/
|
||||
public abstract void tick();
|
||||
|
||||
/**
|
||||
* @return The name of the service.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isActive()
|
||||
{
|
||||
return isActive;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user