updates 🎱

This commit is contained in:
Paul Reilly 2023-05-30 17:39:54 -05:00
parent b95a06fa7c
commit def84bd747
63 changed files with 1469 additions and 1195 deletions

View File

@ -6,18 +6,20 @@ import org.bukkit.plugin.java.JavaPlugin;
public class Corvo extends JavaPlugin public class Corvo extends JavaPlugin
{ {
@Override @Override
public void onEnable() { public void onDisable()
CommonsBase.getInstance() {
.getRegistrations()
.getModuleRegistry()
.addModule(this);
}
@Override
public void onDisable() {
CommonsBase.getInstance() CommonsBase.getInstance()
.getRegistrations() .getRegistrations()
.getModuleRegistry() .getModuleRegistry()
.removeModule(this); .removeModule(this);
} }
@Override
public void onEnable()
{
CommonsBase.getInstance()
.getRegistrations()
.getModuleRegistry()
.addModule(this);
}
} }

View File

@ -24,14 +24,36 @@ public class Datura extends JavaPlugin
.getModuleRegistry() .getModuleRegistry()
.addModule(this); .addModule(this);
CommonsBase.getInstance().getRegistrations().getServiceRegistry().register(this, locker); CommonsBase.getInstance()
CommonsBase.getInstance().getRegistrations().getServiceRegistry().register(this, cager); .getRegistrations()
.getServiceRegistry()
.register(this, locker);
CommonsBase.getInstance()
.getRegistrations()
.getServiceRegistry()
.register(this, cager);
Bukkit.getPluginManager().registerEvents(halter, this); Bukkit.getPluginManager()
.registerEvents(halter, this);
} }
public MySQL getSQL() public MySQL getSQL()
{ {
return sql; return sql;
} }
public Halter getHalter()
{
return halter;
}
public Locker getLocker()
{
return locker;
}
public Cager getCager()
{
return cager;
}
} }

View File

@ -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';
}
}

View File

@ -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;
}
}

View File

@ -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.");
}
}

View File

@ -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() + ".");
}
}
}

View File

@ -5,10 +5,6 @@ import me.totalfreedom.security.perm.NodeType;
public class DefaultNodes public class DefaultNodes
{ {
private DefaultNodes() {
throw new AssertionError();
}
public static final Node OP = new PermissionNodeBuilder() public static final Node OP = new PermissionNodeBuilder()
.key("freedom.master_key") .key("freedom.master_key")
.value(true) .value(true)
@ -16,7 +12,6 @@ public class DefaultNodes
.negated(false) .negated(false)
.wildcard(true) .wildcard(true)
.build(); .build();
public static final Node NON_OP = new PermissionNodeBuilder() public static final Node NON_OP = new PermissionNodeBuilder()
.key("freedom.default") .key("freedom.default")
.value(true) .value(true)
@ -24,7 +19,6 @@ public class DefaultNodes
.negated(false) .negated(false)
.wildcard(false) .wildcard(false)
.build(); .build();
public static final Node ALL = new PermissionNodeBuilder() public static final Node ALL = new PermissionNodeBuilder()
.key("*") .key("*")
.value(true) .value(true)
@ -32,7 +26,6 @@ public class DefaultNodes
.negated(false) .negated(false)
.wildcard(true) .wildcard(true)
.build(); .build();
public static final Node NONE = new PermissionNodeBuilder() public static final Node NONE = new PermissionNodeBuilder()
.key("freedom.none") .key("freedom.none")
.value(true) .value(true)
@ -40,4 +33,9 @@ public class DefaultNodes
.negated(false) .negated(false)
.wildcard(false) .wildcard(false)
.build(); .build();
private DefaultNodes()
{
throw new AssertionError();
}
} }

View File

@ -44,6 +44,12 @@ public class FreedomGroup implements Group
this.attachment = new PermissionAttachment(CommonsBase.getInstance(), this); this.attachment = new PermissionAttachment(CommonsBase.getInstance(), this);
} }
@Override
public UUID getUniqueId()
{
return UUID.nameUUIDFromBytes(getName().toString()
.getBytes());
}
@Override @Override
public Component getName() public Component getName()
@ -81,12 +87,6 @@ public class FreedomGroup implements Group
return isHidden; return isHidden;
} }
@Override
public UUID getUniqueId()
{
return UUID.nameUUIDFromBytes(getName().toString().getBytes());
}
@Override @Override
public Set<Node> permissions() public Set<Node> permissions()
{ {
@ -109,7 +109,8 @@ public class FreedomGroup implements Group
public boolean isPermissionSet(@NotNull final String name) public boolean isPermissionSet(@NotNull final String name)
{ {
final Node node = permissions().stream() final Node node = permissions().stream()
.filter(n -> n.key().equalsIgnoreCase(name)) .filter(n -> n.key()
.equalsIgnoreCase(name))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -121,7 +122,8 @@ public class FreedomGroup implements Group
{ {
final Node node = permissions() final Node node = permissions()
.stream() .stream()
.filter(n -> n.bukkit().equals(perm)) .filter(n -> n.bukkit()
.equals(perm))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -132,7 +134,8 @@ public class FreedomGroup implements Group
public boolean hasPermission(@NotNull final String name) public boolean hasPermission(@NotNull final String name)
{ {
final Node node = permissions().stream() final Node node = permissions().stream()
.filter(n -> n.key().equalsIgnoreCase(name)) .filter(n -> n.key()
.equalsIgnoreCase(name))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -144,7 +147,8 @@ public class FreedomGroup implements Group
{ {
final Node node = permissions() final Node node = permissions()
.stream() .stream()
.filter(n -> n.bukkit().equals(perm)) .filter(n -> n.bukkit()
.equals(perm))
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -164,7 +168,8 @@ public class FreedomGroup implements Group
* @return This group's PermissionAttachment. * @return This group's PermissionAttachment.
*/ */
@Override @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); attachment.setPermission(name, value);
return attachment; return attachment;
@ -177,7 +182,8 @@ public class FreedomGroup implements Group
} }
@Override @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); attachment.setPermission(name, value);
return attachment; return attachment;

View File

@ -64,10 +64,23 @@ public class FreedomUser implements User
} }
@Override @Override
public UserData getUserData() { public UserData getUserData()
{
return userData; return userData;
} }
@Override
public Component getDisplayName()
{
return displayName;
}
@Override
public boolean isOnline()
{
return Bukkit.getPlayer(uuid) != null;
}
@Override @Override
public UUID getUniqueId() public UUID getUniqueId()
{ {
@ -96,18 +109,6 @@ public class FreedomUser implements User
return permissions.remove(node); return permissions.remove(node);
} }
@Override
public Component getDisplayName()
{
return displayName;
}
@Override
public boolean isOnline()
{
return Bukkit.getPlayer(uuid) != null;
}
@Override @Override
public boolean isPermissionSet(@NotNull final String name) public boolean isPermissionSet(@NotNull final String name)
{ {
@ -137,7 +138,8 @@ public class FreedomUser implements User
} }
@Override @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); final Player player = Bukkit.getPlayer(uuid);
if (player != null) if (player != null)
@ -161,7 +163,8 @@ public class FreedomUser implements User
} }
@Override @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); final Player player = Bukkit.getPlayer(uuid);
if (player != null) if (player != null)

View File

@ -9,21 +9,23 @@ record PermissionNode(String key,
boolean value, boolean value,
long expiry, long expiry,
NodeType type, NodeType type,
boolean wildcard, boolean wildcard) implements Node
boolean negated) implements Node
{ {
@Override @Override
public Permission bukkit() public Permission bukkit()
{ {
return new Permission(key(), return new Permission(key(),
value() ? PermissionDefault.TRUE : PermissionDefault.FALSE); value()
? PermissionDefault.TRUE
: PermissionDefault.FALSE);
} }
@Override @Override
public boolean compare(final Node node) public boolean compare(final Node node)
{ {
return node.key().equalsIgnoreCase(key()) return node.key()
.equalsIgnoreCase(key())
&& node.value() == value() && node.value() == value()
&& node.type() == type(); && node.type() == type();
} }
@ -31,7 +33,7 @@ record PermissionNode(String key,
@Override @Override
public boolean isExpired() public boolean isExpired()
{ {
if (isPermanent()) if (!isTemporary())
{ {
return false; return false;
} }
@ -39,15 +41,9 @@ record PermissionNode(String key,
return System.currentTimeMillis() > expiry(); return System.currentTimeMillis() > expiry();
} }
@Override
public boolean isPermanent()
{
return expiry() == -1;
}
@Override @Override
public boolean isTemporary() public boolean isTemporary()
{ {
return !isPermanent(); return expiry() > -1;
} }
} }

View File

@ -14,8 +14,8 @@ import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -31,7 +31,8 @@ public class Cager extends Service
super("cage_service"); super("cage_service");
this.cagedPlayers = new HashSet<>(); this.cagedPlayers = new HashSet<>();
this.cageLocations = new HashMap<>(); this.cageLocations = new HashMap<>();
Bukkit.getPluginManager().registerEvents(new CageListener(), CommonsBase.getInstance()); Bukkit.getPluginManager()
.registerEvents(new CageListener(), CommonsBase.getInstance());
} }
/** /**
@ -49,6 +50,39 @@ public class Cager extends Service
cageLocations.put(uuid, createCage(player.getLocation(), Material.GLASS)); 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 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.
}
public void cagePlayer(final UUID uuid, final Material material) public void cagePlayer(final UUID uuid, final Material material)
{ {
final Player player = Bukkit.getPlayer(uuid); final Player player = Bukkit.getPlayer(uuid);
@ -92,12 +126,14 @@ public class Cager extends Service
final Location cageLocation = getCageLocation(player); final Location cageLocation = getCageLocation(player);
final boolean inside; final boolean inside;
if (!player.getWorld().equals(cageLocation.getWorld())) if (!player.getWorld()
.equals(cageLocation.getWorld()))
{ {
inside = false; inside = false;
} else } 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) if (!inside)
@ -119,43 +155,13 @@ public class Cager extends Service
return cageLocations.get(player.getUniqueId()); 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 private final class CageListener implements Listener
{ {
@EventHandler @EventHandler
public void blockBreakEvent(final BlockBreakEvent event) public void blockBreakEvent(final BlockBreakEvent event)
{ {
if (cagedPlayers.contains(event.getPlayer().getUniqueId())) if (cagedPlayers.contains(event.getPlayer()
.getUniqueId()))
{ {
event.setCancelled(true); event.setCancelled(true);
} }
@ -164,9 +170,11 @@ public class Cager extends Service
@EventHandler @EventHandler
public void playerLeaveEvent(final PlayerQuitEvent event) 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());
} }
} }
} }

View File

@ -25,7 +25,8 @@ public class Halter implements Listener
@EventHandler @EventHandler
public void playerMove(final PlayerMoveEvent event) public void playerMove(final PlayerMoveEvent event)
{ {
if (haltedPlayers.contains(event.getPlayer().getUniqueId())) if (haltedPlayers.contains(event.getPlayer()
.getUniqueId()))
{ {
event.setCancelled(true); event.setCancelled(true);
} }

View File

@ -22,15 +22,23 @@ public class Locker extends Service
super("locker-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 @Override
public void tick() 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) for (final UUID uuid : lockedPlayers)
{ {
@ -43,8 +51,10 @@ public class Locker extends Service
private void lockingMethod(@NotNull final Player player) private void lockingMethod(@NotNull final Player player)
{ {
final double x = player.getLocation().getX(); final double x = player.getLocation()
final double z = player.getLocation().getZ(); .getX();
final double z = player.getLocation()
.getZ();
if ((x / z % 0.001) < 1) if ((x / z % 0.001) < 1)
{ {
@ -65,10 +75,12 @@ public class Locker extends Service
player.openInventory(Bukkit.createInventory(null, 54)); player.openInventory(Bukkit.createInventory(null, 54));
player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN); player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN);
player.teleport(player.getLocation().clone()); player.teleport(player.getLocation()
.clone());
final SplittableRandom random = new SplittableRandom(); 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), random.nextDouble(-1.0, 1.0),
random.nextDouble(-1.0, 1.0) random.nextDouble(-1.0, 1.0)

View File

@ -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();
}
}

View File

@ -2,6 +2,7 @@ package me.totalfreedom.datura.sql;
import me.totalfreedom.base.CommonsBase; import me.totalfreedom.base.CommonsBase;
import me.totalfreedom.sql.SQL; import me.totalfreedom.sql.SQL;
import me.totalfreedom.utils.Identity;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
@ -13,10 +14,18 @@ import java.util.concurrent.CompletionException;
public class MySQL implements SQL 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) { public MySQL(final String host, final int port, final String database)
url += host + ":" + port + "/" + database; {
url.append(host)
.append(':')
.append(port)
.append('/')
.append(database);
} }
/** /**
@ -26,85 +35,130 @@ public class MySQL implements SQL
* @param username The username to add * @param username The username to add
* @param password The password to add * @param password The password to add
*/ */
public void addCredentials(final String username, final String password) { 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)
{ {
return CompletableFuture.supplyAsync(() -> { if (url.toString()
try { .contains("?user="))
return DriverManager.getConnection(url); {
} catch (SQLException ex) { final String split = url.toString()
throw new CompletionException("Failed to connect to the database: " .split("\\x3f")[0];
+ url + "\n", ex); 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 @Override
public CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args) public CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args)
{ {
return getConnection(url) return getConnection()
.thenApplyAsync(connection -> { .thenApplyAsync(connection ->
try { {
try
{
final PreparedStatement statement = connection.prepareStatement(query); 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]); statement.setObject(i + 1, args[i]);
} }
return statement; return statement;
} catch (SQLException ex) { }
catch (SQLException ex)
{
throw new CompletionException("Failed to prepare statement: " throw new CompletionException("Failed to prepare statement: "
+ query + "\n", ex); + 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 @Override
public CompletableFuture<ResultSet> executeQuery(final String query, final Object... args) public CompletableFuture<ResultSet> executeQuery(final String query, final Object... args)
{ {
return prepareStatement(query, args) return prepareStatement(query, args)
.thenApplyAsync(statement -> { .thenApplyAsync(statement ->
try { {
try
{
return statement.executeQuery(); 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); + query + "\n", ex);
} }
}, CommonsBase.getInstance().getExecutor().getAsync()); }, CommonsBase.getInstance()
.getExecutor()
.getAsync());
} }
@Override @Override
public CompletableFuture<Integer> executeUpdate(final String query, final Object... args) public CompletableFuture<Integer> executeUpdate(final String query, final Object... args)
{ {
return prepareStatement(query, args) return prepareStatement(query, args)
.thenApplyAsync(statement -> { .thenApplyAsync(statement ->
try { {
try
{
return statement.executeUpdate(); return statement.executeUpdate();
} catch (SQLException ex) { }
catch (SQLException ex)
{
throw new CompletionException("Failed to execute update: " throw new CompletionException("Failed to execute update: "
+ query + "\n", ex); + query + "\n", ex);
} }
}, CommonsBase.getInstance().getExecutor().getAsync()); }, CommonsBase.getInstance()
.getExecutor()
.getAsync());
} }
@Override @Override
public CompletableFuture<Boolean> execute(final String query, final Object... args) public CompletableFuture<Boolean> execute(final String query, final Object... args)
{ {
return prepareStatement(query, args) return prepareStatement(query, args)
.thenApplyAsync(statement -> { .thenApplyAsync(statement ->
try { {
try
{
return statement.execute(); return statement.execute();
} catch (SQLException ex) { }
catch (SQLException ex)
{
throw new CompletionException("Failed to execute statement: " throw new CompletionException("Failed to execute statement: "
+ query + "\n", ex); + query + "\n", ex);
} }
}, CommonsBase.getInstance().getExecutor().getAsync()); }, CommonsBase.getInstance()
.getExecutor()
.getAsync());
} }
@Override @Override
@ -113,9 +167,11 @@ public class MySQL implements SQL
final StringBuilder query = new StringBuilder(); final StringBuilder query = new StringBuilder();
query.append("CREATE TABLE IF NOT EXISTS ? ("); 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("?"); query.append("?");
if (i != columns.length - 1) { if (i != columns.length - 1)
{
query.append(", "); query.append(", ");
} }
} }
@ -123,4 +179,90 @@ public class MySQL implements SQL
return execute(query.toString(), table, columns); 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);
}
} }

View 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)
{
}

View File

@ -38,7 +38,9 @@ public class SimpleUserData implements UserData
this.username = player.getName(); this.username = player.getName();
this.user = new FreedomUser(player); this.user = new FreedomUser(player);
CommonsBase.getInstance().getEventBus().addEvent(event); CommonsBase.getInstance()
.getEventBus()
.addEvent(event);
} }
private SimpleUserData( private SimpleUserData(
@ -82,7 +84,8 @@ public class SimpleUserData implements UserData
final Player player = Bukkit.getPlayer(u); final Player player = Bukkit.getPlayer(u);
if (player == null) if (player == null)
throw new IllegalStateException("Player should be online but they are not!"); throw new IllegalStateException(
"Player should be online but they are not!");
final User user = new FreedomUser(player); final User user = new FreedomUser(player);
final Group group = CommonsBase.getInstance() final Group group = CommonsBase.getInstance()
@ -95,12 +98,17 @@ public class SimpleUserData implements UserData
final boolean canInteract = result.getBoolean("canInteract"); final boolean canInteract = result.getBoolean("canInteract");
final boolean caged = result.getBoolean("caged"); final boolean caged = result.getBoolean("caged");
final long balance = result.getLong("balance"); final long balance = result.getLong("balance");
final boolean transactionsFrozen = result.getBoolean("transactionsFrozen"); final boolean transactionsFrozen = result.getBoolean(
return new SimpleUserData(u, username, user, group, playtime, frozen, canInteract, caged, balance, transactionsFrozen); "transactionsFrozen");
return new SimpleUserData(u, username, user, group, playtime, frozen,
canInteract, caged, balance,
transactionsFrozen);
} }
} catch (SQLException ex) }
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 + uuid +
" from the database." + " from the database." +
"\nCaused by: " + "\nCaused by: " +
@ -113,7 +121,8 @@ public class SimpleUserData implements UserData
} }
final Player player = Bukkit.getPlayer(UUID.fromString(uuid)); final Player player = Bukkit.getPlayer(UUID.fromString(uuid));
if (player == null) throw new IllegalStateException("Player should be online but they are not!"); if (player == null) throw new IllegalStateException(
"Player should be online but they are not!");
return new SimpleUserData(player); return new SimpleUserData(player);
}, CommonsBase.getInstance() }, CommonsBase.getInstance()
.getExecutor() .getExecutor()
@ -230,6 +239,12 @@ public class SimpleUserData implements UserData
return balance.get(); return balance.get();
} }
@Override
public void setBalance(final long newBalance)
{
balance.set(newBalance);
}
@Override @Override
public long addToBalance(final long amount) public long addToBalance(final long amount)
{ {
@ -241,10 +256,4 @@ public class SimpleUserData implements UserData
{ {
return balance.addAndGet(-amount); return balance.addAndGet(-amount);
} }
@Override
public void setBalance(final long newBalance)
{
balance.set(newBalance);
}
} }

View File

@ -7,9 +7,9 @@ import java.util.concurrent.atomic.AtomicLong;
public class SimpleTransaction implements Transaction public class SimpleTransaction implements Transaction
{ {
protected final AtomicLong balance;
private final EconomicEntity source; private final EconomicEntity source;
private final EconomicEntity destination; private final EconomicEntity destination;
protected final AtomicLong balance;
public SimpleTransaction(final EconomicEntity source, final EconomicEntity destination, final long balance) public SimpleTransaction(final EconomicEntity source, final EconomicEntity destination, final long balance)
{ {

View File

@ -1,10 +1,10 @@
package me.totalfreedom.fossil.economy; package me.totalfreedom.fossil.economy;
import me.totalfreedom.audience.MutableAudienceForwarder; import me.totalfreedom.audience.MutableAudienceForwarder;
import me.totalfreedom.economy.TransactionResult;
import me.totalfreedom.economy.CompletedTransaction; import me.totalfreedom.economy.CompletedTransaction;
import me.totalfreedom.economy.TransactionLogger;
import me.totalfreedom.economy.EconomicEntity; import me.totalfreedom.economy.EconomicEntity;
import me.totalfreedom.economy.TransactionLogger;
import me.totalfreedom.economy.TransactionResult;
import me.totalfreedom.utils.FreedomLogger; import me.totalfreedom.utils.FreedomLogger;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@ -24,7 +24,9 @@ public class SimpleTransactionLogger implements TransactionLogger
final EconomicEntity destination = completedTransaction.getDestination(); final EconomicEntity destination = completedTransaction.getDestination();
final long transactionAmount = completedTransaction.getBalance(); final long transactionAmount = completedTransaction.getBalance();
transactionLoggingStatementBuilder.append(resultSuccess ? "Successful" : "Unsuccessful") transactionLoggingStatementBuilder.append(resultSuccess
? "Successful"
: "Unsuccessful")
.append(" (") .append(" (")
.append(resultMessage) .append(resultMessage)
.append(") ") .append(") ")

View File

@ -7,16 +7,21 @@ import net.kyori.adventure.text.format.NamedTextColor;
public class SimpleTransactionResult implements TransactionResult public class SimpleTransactionResult implements TransactionResult
{ {
public static final TransactionResult SUCCESSFUL = new SimpleTransactionResult("Successful transaction.", true); public static final TransactionResult SUCCESSFUL = new SimpleTransactionResult("Successful transaction.", true);
public static final TransactionResult UNAUTHORIZED = new SimpleTransactionResult("Unauthorized transaction.", false); public static final TransactionResult UNAUTHORIZED = new SimpleTransactionResult("Unauthorized transaction.",
public static final TransactionResult AMOUNT_TOO_SMALL = new SimpleTransactionResult("Transaction balance too small.", false); 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 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 String message;
private final Component component; private final Component component;
private final boolean successful; private final boolean successful;
public SimpleTransactionResult(final String message, 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) public SimpleTransactionResult(final String message, final Component component, final boolean successful)

View File

@ -0,0 +1,5 @@
package me.totalfreedom.fossil.shop;
public class Shoppe
{
}

View File

@ -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);
}
}

View File

@ -16,13 +16,13 @@ import java.util.function.Function;
@FunctionalInterface @FunctionalInterface
public interface Context<T> public interface Context<T>
{ {
T get();
default <S> Context<S> map(@NotNull final Function<T, S> mapper) default <S> Context<S> map(@NotNull final Function<T, S> mapper)
{ {
return () -> mapper.apply(get()); return () -> mapper.apply(get());
} }
T get();
default @Nullable String asString() default @Nullable String asString()
{ {
if (get() instanceof String string) if (get() instanceof String string)
@ -56,26 +56,35 @@ public interface Context<T>
} }
} }
default @Nullable Integer asInt() { default @Nullable Integer asInt()
if (get() instanceof Integer integer) { {
if (get() instanceof Integer integer)
{
return integer; return integer;
} else { } else
{
return null; return null;
} }
} }
default @Nullable Long asLong() { default @Nullable Long asLong()
if (get() instanceof Long longg) { {
if (get() instanceof Long longg)
{
return longg; return longg;
} else { } else
{
return null; return null;
} }
} }
default @Nullable Float asFloat() { default @Nullable Float asFloat()
if (get() instanceof Float floatt) { {
if (get() instanceof Float floatt)
{
return floatt; return floatt;
} else { } else
{
return null; return null;
} }
} }

View File

@ -19,7 +19,8 @@ import java.util.function.Consumer;
import java.util.function.Predicate; 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> * <p>
* This is intended for use in toggleable logging systems, for example, potion spy. * This is intended for use in toggleable logging systems, for example, potion spy.
*/ */

View File

@ -15,33 +15,36 @@ public class CommonsBase extends JavaPlugin
return JavaPlugin.getPlugin(CommonsBase.class); return JavaPlugin.getPlugin(CommonsBase.class);
} }
@Override
public void onDisable()
{
getRegistrations().getServiceRegistry()
.stopAll();
getRegistrations().getServiceRegistry()
.unregister(EventBus.class, eventBus);
}
@Override @Override
public void onEnable() public void onEnable()
{ {
getRegistrations().getServiceRegistry().register(this, eventBus); getRegistrations().getServiceRegistry()
.register(this, eventBus);
getExecutor().getSync() getExecutor().getSync()
.execute(() -> getRegistrations() .execute(() -> getRegistrations()
.getServiceRegistry() .getServiceRegistry()
.startAll()); .startAll());
} }
@Override
public void onDisable()
{
getRegistrations().getServiceRegistry().stopAll();
getRegistrations().getServiceRegistry().unregister(EventBus.class, eventBus);
}
public Registration getRegistrations()
{
return registration;
}
public FreedomExecutor getExecutor() public FreedomExecutor getExecutor()
{ {
return executor; return executor;
} }
public Registration getRegistrations()
{
return registration;
}
public EventBus getEventBus() public EventBus getEventBus()
{ {
return eventBus; return eventBus;

View File

@ -1,12 +1,11 @@
package me.totalfreedom.base; package me.totalfreedom.base;
import me.totalfreedom.data.GroupRegistry;
import me.totalfreedom.data.BanRegistry;
import me.totalfreedom.data.ConfigRegistry; import me.totalfreedom.data.ConfigRegistry;
import me.totalfreedom.data.EventRegistry;
import me.totalfreedom.data.GroupRegistry;
import me.totalfreedom.data.ModuleRegistry; import me.totalfreedom.data.ModuleRegistry;
import me.totalfreedom.data.ServiceRegistry; import me.totalfreedom.data.ServiceRegistry;
import me.totalfreedom.data.UserRegistry; import me.totalfreedom.data.UserRegistry;
import me.totalfreedom.data.EventRegistry;
public class Registration public class Registration
{ {
@ -15,7 +14,6 @@ public class Registration
private final ServiceRegistry serviceRegistry; private final ServiceRegistry serviceRegistry;
private final ModuleRegistry moduleRegistry; private final ModuleRegistry moduleRegistry;
private final GroupRegistry groupRegistry; private final GroupRegistry groupRegistry;
private final BanRegistry banRegistry;
private final ConfigRegistry configRegistry; private final ConfigRegistry configRegistry;
public Registration() public Registration()
@ -25,7 +23,6 @@ public class Registration
this.serviceRegistry = new ServiceRegistry(); this.serviceRegistry = new ServiceRegistry();
this.moduleRegistry = new ModuleRegistry(); this.moduleRegistry = new ModuleRegistry();
this.groupRegistry = new GroupRegistry(); this.groupRegistry = new GroupRegistry();
this.banRegistry = new BanRegistry();
this.configRegistry = new ConfigRegistry(); this.configRegistry = new ConfigRegistry();
} }
@ -54,11 +51,6 @@ public class Registration
return groupRegistry; return groupRegistry;
} }
public BanRegistry getBanRegistry()
{
return banRegistry;
}
public ConfigRegistry getConfigRegistry() public ConfigRegistry getConfigRegistry()
{ {
return configRegistry; return configRegistry;

View File

@ -0,0 +1,188 @@
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 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -12,14 +12,11 @@ public class CommandHandler
this.plugin = plugin; this.plugin = plugin;
} }
// TODO: Figure out how to use CommandExecutor and TabCompleter. public <T extends Commander> void registerCommand(final T command)
// 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)
{ {
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);
} }
} }

View File

@ -0,0 +1,108 @@
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;
public abstract class Commander
{
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 Method baseMethod;
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();
}
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);
}
@Nullable
public Method getBaseMethod()
{
return baseMethod;
}
@NotNull
Info getInfo()
{
return this.info;
}
@NotNull
Permissive getPerms()
{
return this.perms;
}
@NotNull
public JavaPlugin getPlugin()
{
return this.plugin;
}
@NotNull
Map<Subcommand, Method> getSubcommands()
{
return this.subcommands;
}
@Nullable
Set<Completion> getCompletions()
{
return this.completions;
}
}

View File

@ -1,11 +1,13 @@
package me.totalfreedom.command.annotation; package me.totalfreedom.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Target(ElementType.METHOD)
@Repeatable(Completions.class)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Completion public @interface Completion
{ {

View File

@ -0,0 +1,13 @@
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;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Completions
{
Completion[] value();
}

View File

@ -1,8 +1,11 @@
package me.totalfreedom.command.annotation; package me.totalfreedom.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Subcommand public @interface Subcommand
{ {

View File

@ -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;
}
}

View File

@ -15,26 +15,32 @@ public class GroupRegistry
this.groups = new ArrayList<>(); this.groups = new ArrayList<>();
} }
public boolean registerGroup(final Group group) { public boolean registerGroup(final Group group)
{
return groups.add(group); return groups.add(group);
} }
public boolean unregisterGroup(final Group group) { public boolean unregisterGroup(final Group group)
{
return groups.remove(group); return groups.remove(group);
} }
public Group getGroup(final String name) { public Group getGroup(final String name)
{
final PlainTextComponentSerializer s = PlainTextComponentSerializer.plainText(); final PlainTextComponentSerializer s = PlainTextComponentSerializer.plainText();
for (final Group group : groups) { for (final Group group : groups)
{
final String n = s.serialize(group.getName()); final String n = s.serialize(group.getName());
if (n.equalsIgnoreCase(name)) { if (n.equalsIgnoreCase(name))
{
return group; return group;
} }
} }
return null; return null;
} }
public List<Group> getGroups() { public List<Group> getGroups()
{
return groups; return groups;
} }
} }

View File

@ -24,7 +24,8 @@ public class ModuleRegistry
this.plugins.add(plugin); this.plugins.add(plugin);
} }
public void removeModule(final JavaPlugin plugin) { public void removeModule(final JavaPlugin plugin)
{
this.plugins.remove(plugin); this.plugins.remove(plugin);
} }

View File

@ -42,15 +42,18 @@ public class ServiceRegistry
public <T extends Service> void register(final Plugin plugin, final T service) public <T extends Service> void register(final Plugin plugin, final T service)
{ {
this.services.add(service); this.services.add(service);
if (!service.getClass().isInstance(service)) if (!service.getClass()
.isInstance(service))
{ {
throw new UnknownError(""" throw new UnknownError("""
A critical issue has been encountered: A critical issue has been encountered:
The service %s is not an instance of itself. The service %s is not an instance of itself.
This is a critical issue and should be reported immediately. This is a critical issue and should be reported immediately.
""".formatted(service.getClass().getName())); """.formatted(service.getClass()
.getName()));
} }
Bukkit.getServicesManager().register( Bukkit.getServicesManager()
.register(
(Class<T>) service.getClass(), (Class<T>) service.getClass(),
service, service,
plugin, plugin,
@ -59,12 +62,14 @@ public class ServiceRegistry
public <T extends Service> RegisteredServiceProvider<T> getService(final Class<T> clazz) public <T extends Service> RegisteredServiceProvider<T> getService(final Class<T> clazz)
{ {
return Bukkit.getServicesManager().getRegistration(clazz); return Bukkit.getServicesManager()
.getRegistration(clazz);
} }
public void unregister(final Class<? extends Service> clazz, final Service service) public void unregister(final Class<? extends Service> clazz, final Service service)
{ {
this.services.remove(service); this.services.remove(service);
Bukkit.getServicesManager().unregister(clazz, service); Bukkit.getServicesManager()
.unregister(clazz, service);
} }
} }

View File

@ -0,0 +1,121 @@
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;
public abstract class AbstractMenu
{
private static final Map<UUID, AbstractMenu> invByUUID = new HashMap<>();
private static final Map<UUID, UUID> openInvs = new HashMap<>();
private final Map<Integer, ClickAction> actionMap;
private final Displayable displayable;
private final UUID displayableUUID;
protected AbstractMenu(final int size)
{
actionMap = new HashMap<>();
this.displayable = new Displayable(size);
this.displayableUUID = UUID.randomUUID();
invByUUID.put(getDisplayableUUID(), this);
}
public UUID getDisplayableUUID()
{
return displayableUUID;
}
public static Map<UUID, AbstractMenu> getInvByUUID()
{
return invByUUID;
}
public static Map<UUID, UUID> getOpenInvs()
{
return openInvs;
}
public void setItem(final int slot, final @NotNull ItemStack stack)
{
setItem(slot, stack, null);
}
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);
}
}
public Displayable getDisplayable()
{
return displayable;
}
public void open(final @NotNull Player player)
{
player.openInventory(getDisplayable());
openInvs.put(player.getUniqueId(), getDisplayableUUID());
}
public void delete()
{
Bukkit.getOnlinePlayers()
.forEach(player ->
{
if (openInvs.get(player.getUniqueId())
.equals(getDisplayableUUID()))
{
close(player);
}
});
invByUUID.remove(getDisplayableUUID());
}
public void close(final @NotNull Player player)
{
player.closeInventory();
openInvs.remove(player.getUniqueId());
}
public Map<Integer, ClickAction> getActions()
{
return actionMap;
}
public ItemStack newItem(final @NotNull Material material, final @NotNull Component name)
{
return this.newItem(material, name, new Component[0]);
}
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;
}
}

View File

@ -0,0 +1,9 @@
package me.totalfreedom.display;
import org.bukkit.entity.Player;
@FunctionalInterface
public interface ClickAction
{
void onClick(final Player player);
}

View File

@ -1,5 +1,6 @@
package me.totalfreedom.display; package me.totalfreedom.display;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
@ -14,11 +15,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
public abstract class Displayable implements Inventory, InventoryHolder public final class Displayable implements Inventory, InventoryHolder
{ {
private final int size; private final int size;
private final ItemStack[] contents; private ItemStack[] contents;
protected Displayable(final int size) protected Displayable(final int size)
{ {
@ -27,9 +28,9 @@ public abstract class Displayable implements Inventory, InventoryHolder
throw new IllegalArgumentException("Invalid size for Displayable inventory"); throw new IllegalArgumentException("Invalid size for Displayable inventory");
} }
this.size = (size % 9 == 0) // If the size is not a multiple of nine, find the difference to the next highest multiple of 9 and make up
? size // the difference.
: size + (9 - size % 9); this.size = (size % 9 == 0) ? size : size + (9 - size % 9);
this.contents = new ItemStack[size]; this.contents = new ItemStack[size];
} }
@ -78,7 +79,8 @@ public abstract class Displayable implements Inventory, InventoryHolder
} }
@Override @Override
public @NotNull HashMap<Integer, ItemStack> addItem(final @NotNull ItemStack... items) throws IllegalArgumentException public @NotNull HashMap<Integer, ItemStack> addItem(final @NotNull ItemStack... items)
throws IllegalArgumentException
{ {
final HashMap<Integer, ItemStack> remainingItems = new HashMap<>(); final HashMap<Integer, ItemStack> remainingItems = new HashMap<>();
@ -116,7 +118,8 @@ public abstract class Displayable implements Inventory, InventoryHolder
} }
@Override @Override
public @NotNull HashMap<Integer, ItemStack> removeItem(final @NotNull ItemStack... items) throws IllegalArgumentException public @NotNull HashMap<Integer, ItemStack> removeItem(final @NotNull ItemStack... items)
throws IllegalArgumentException
{ {
final HashMap<Integer, ItemStack> removedItems = new HashMap<>(); final HashMap<Integer, ItemStack> removedItems = new HashMap<>();
@ -148,13 +151,21 @@ public abstract class Displayable implements Inventory, InventoryHolder
if (remainingAmount < item.getAmount()) if (remainingAmount < item.getAmount())
{ {
removedItems.put(removedItems.size(), new ItemStack(item.getType(), item.getAmount() - remainingAmount)); removedItems.put(removedItems.size(),
new ItemStack(item.getType(), item.getAmount() - remainingAmount));
} }
} }
return removedItems; return removedItems;
} }
@Override
public @NotNull HashMap<Integer, ItemStack> removeItemAnySlot(final @NotNull ItemStack... items)
throws IllegalArgumentException
{
return removeItem(items);
}
@Override @Override
public @Nullable ItemStack @NotNull [] getContents() public @Nullable ItemStack @NotNull [] getContents()
{ {
@ -176,39 +187,15 @@ public abstract class Displayable implements Inventory, InventoryHolder
} }
@Override @Override
public @NotNull ListIterator<ItemStack> iterator() public @Nullable ItemStack @NotNull [] getStorageContents()
{ {
return iterator(0); return contents;
} }
@Override @Override
public @NotNull ListIterator<ItemStack> iterator(final int index) public void setStorageContents(final @Nullable ItemStack @NotNull [] items) throws IllegalArgumentException
{ {
return List.of(contents).listIterator(index); this.contents = items;
}
@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 List<HumanEntity> getViewers()
{
return new ArrayList<>();
} }
@Override @Override
@ -272,7 +259,7 @@ public abstract class Displayable implements Inventory, InventoryHolder
if (content != null && content.isSimilar(item)) if (content != null && content.isSimilar(item))
{ {
totalAmount += content.getAmount(); totalAmount += content.getAmount();
if (totalAmount >= amount) if (totalAmount == amount)
{ {
return true; return true;
} }
@ -304,7 +291,8 @@ public abstract class Displayable implements Inventory, InventoryHolder
} }
@Override @Override
public @NotNull HashMap<Integer, ? extends ItemStack> all(final @NotNull Material material) throws IllegalArgumentException public @NotNull HashMap<Integer, ? extends ItemStack> all(final @NotNull Material material)
throws IllegalArgumentException
{ {
final HashMap<Integer, ItemStack> matchingItems = new HashMap<>(); final HashMap<Integer, ItemStack> matchingItems = new HashMap<>();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
@ -439,4 +427,53 @@ public abstract class Displayable implements Inventory, InventoryHolder
{ {
return 0; 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;
}
} }

View File

@ -16,7 +16,8 @@ public class DisplayableView extends InventoryView
private final InventoryType type; private final InventoryType type;
private String title; private String title;
public DisplayableView(final Player player, final Displayable top, final Displayable bottom) { public DisplayableView(final Player player, final Displayable top, final Displayable bottom)
{
this.player = player; this.player = player;
this.top = top; this.top = top;
this.bottom = bottom; this.bottom = bottom;

View File

@ -6,9 +6,9 @@ public interface EconomicEntityData
long getBalance(); long getBalance();
void setBalance(final long newBalance);
long addToBalance(final long amount); long addToBalance(final long amount);
long removeFromBalance(final long amount); long removeFromBalance(final long amount);
void setBalance(final long newBalance);
} }

View File

@ -1,7 +1,8 @@
package me.totalfreedom.economy; package me.totalfreedom.economy;
/** /**
* Please ensure that all modifications of {@link MutableTransaction} happen BEFORE it is passed to a {@link Transactor} implementation * Please ensure that all modifications of {@link MutableTransaction} happen BEFORE it is passed to a
* {@link Transactor} implementation
*/ */
public interface MutableTransaction extends Transaction public interface MutableTransaction extends Transaction
{ {

View File

@ -27,7 +27,8 @@ public class EventBus extends Service
public <T extends FEvent> T getEvent(final Class<T> eventClass) public <T extends FEvent> T getEvent(final Class<T> eventClass)
{ {
final FEvent e = eventSet.stream() final FEvent e = eventSet.stream()
.filter(event -> event.getEventClass().equals(eventClass)) .filter(event -> event.getEventClass()
.equals(eventClass))
.findFirst() .findFirst()
.orElse(null); .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) public <T extends FEvent> EventSubscription<T> subscribe(final Class<T> eventClass, final Callback<T> callback)
{ {
final Context<T> eventContext = () -> eventSet.stream() final Context<T> eventContext = () -> eventSet.stream()
.filter(event -> event.getEventClass().equals(eventClass)) .filter(event -> event.getEventClass()
.equals(eventClass))
.findFirst() .findFirst()
.map(eventClass::cast) .map(eventClass::cast)
.orElse(null); .orElse(null);

View File

@ -7,24 +7,32 @@ class SubscriptionBox<T extends FEvent>
{ {
private final List<EventSubscription<T>> subscriptions; private final List<EventSubscription<T>> subscriptions;
public SubscriptionBox() { public SubscriptionBox()
{
this.subscriptions = new ArrayList<>(); this.subscriptions = new ArrayList<>();
} }
public void addSubscription(final EventSubscription<T> subscription) { public void addSubscription(final EventSubscription<T> subscription)
{
subscriptions.add(subscription); subscriptions.add(subscription);
} }
public void removeSubscription(final EventSubscription<?> subscription) { public void removeSubscription(final EventSubscription<?> subscription)
{
subscriptions.remove(subscription); subscriptions.remove(subscription);
} }
public void tick() { public void tick()
subscriptions.forEach(s -> { {
if (!s.event().shouldCall()) return; subscriptions.forEach(s ->
{
if (!s.event()
.shouldCall()) return;
s.callback().call(s.event()); s.callback()
s.event().reset(); .call(s.event());
s.event()
.reset();
}); });
} }
} }

View File

@ -1,11 +1,31 @@
package me.totalfreedom.particle; package me.totalfreedom.particle;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface Trail public interface Trail
{ {
@NotNull
UUID getAssocPlayerUUID();
// Nullable because the player may not be online and trail selections should be persistent whether they are
// active or not.
@Nullable
Player getAssocPlayer();
@NotNull
TrailType getTrailType(); TrailType getTrailType();
@Nullable Color getColor(); @NotNull
Color getColor();
void setColor(@NotNull Color color);
boolean isActive();
void setActive(boolean active);
} }

View File

@ -14,7 +14,7 @@ import java.util.stream.Stream;
public class ContextProvider 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), return Stream.of(toBoolean(string),
toDouble(string), toDouble(string),
@ -28,7 +28,8 @@ public class ContextProvider
toComponent(string)) toComponent(string))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.findFirst() .findFirst()
.orElse(string); .map(clazz::cast)
.orElse(null);
} }
private @Nullable Boolean toBoolean(final String string) private @Nullable Boolean toBoolean(final String string)
@ -36,7 +37,8 @@ public class ContextProvider
try try
{ {
return Boolean.parseBoolean(string); return Boolean.parseBoolean(string);
} catch (Exception e) }
catch (Exception ignored)
{ {
return null; return null;
} }
@ -47,7 +49,8 @@ public class ContextProvider
try try
{ {
return Double.parseDouble(string); return Double.parseDouble(string);
} catch (Exception e) }
catch (Exception ignored)
{ {
return null; return null;
} }
@ -58,7 +61,8 @@ public class ContextProvider
try try
{ {
return Integer.parseInt(string); return Integer.parseInt(string);
} catch (Exception e) }
catch (Exception ignored)
{ {
return null; return null;
} }
@ -69,7 +73,8 @@ public class ContextProvider
try try
{ {
return Long.parseLong(string); return Long.parseLong(string);
} catch (Exception e) }
catch (Exception ignored)
{ {
return null; return null;
} }
@ -80,7 +85,8 @@ public class ContextProvider
try try
{ {
return Float.parseFloat(string); return Float.parseFloat(string);
} catch (Exception e) }
catch (Exception ignored)
{ {
return null; return null;
} }
@ -91,20 +97,11 @@ public class ContextProvider
return Bukkit.getPlayer(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) private @Nullable World toWorld(final String string)
{ {
return Bukkit.getWorld(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 * When using this method, the input string must be formatted as
* <br> * <br>
@ -127,6 +124,13 @@ public class ContextProvider
return new Location(toWorld(split[0]), x, y, z); 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) private @NotNull Component toComponent(final String string)
{ {
return Component.text(string); return Component.text(string);

View File

@ -19,6 +19,23 @@ public interface BanID
*/ */
String getID(); String getID();
/**
* 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());
}
/** /**
* This method returns the ban type denominator character for the Ban ID. * 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. * This would either be T or P, where T = temporary and P = permanent.
@ -34,19 +51,4 @@ public interface BanID
* @return The numerical tag of this ban ID. * @return The numerical tag of this ban ID.
*/ */
int getNumericalTag(); 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());
}
} }

View File

@ -21,11 +21,7 @@ public interface Node
boolean isExpired(); boolean isExpired();
boolean isPermanent();
boolean isTemporary(); boolean isTemporary();
boolean wildcard(); boolean wildcard();
boolean negated();
} }

View File

@ -13,18 +13,10 @@ public class FreedomExecutor
public FreedomExecutor() public FreedomExecutor()
{ {
syncExecutor = r -> Bukkit.getScheduler().runTask(CommonsBase.getInstance(), r); syncExecutor = r -> Bukkit.getScheduler()
asyncExecutor = r -> Bukkit.getScheduler().runTaskAsynchronously(CommonsBase.getInstance(), r); .runTask(CommonsBase.getInstance(), r);
} asyncExecutor = r -> Bukkit.getScheduler()
.runTaskAsynchronously(CommonsBase.getInstance(), r);
public Executor getSync()
{
return syncExecutor;
}
public Executor getAsync()
{
return asyncExecutor;
} }
public Executor scheduled(final long delay, final long period) public Executor scheduled(final long delay, final long period)
@ -52,8 +44,18 @@ public class FreedomExecutor
getSync().execute(task); getSync().execute(task);
} }
public Executor getSync()
{
return syncExecutor;
}
public void runAsync(@NotNull final Task task) public void runAsync(@NotNull final Task task)
{ {
getAsync().execute(task); getAsync().execute(task);
} }
public Executor getAsync()
{
return asyncExecutor;
}
} }

View File

@ -28,13 +28,13 @@ public abstract class Service
}); });
} }
public abstract void tick();
public void stop() public void stop()
{ {
isActive = false; isActive = false;
} }
public abstract void tick();
public String getName() public String getName()
{ {
return name; return name;

View File

@ -1,14 +1,11 @@
package me.totalfreedom.sql; package me.totalfreedom.sql;
import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public interface SQL public interface SQL
{ {
CompletableFuture<Connection> getConnection(final String url);
CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args); CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args);
CompletableFuture<ResultSet> executeQuery(final String query, final Object... args); CompletableFuture<ResultSet> executeQuery(final String query, final Object... args);

View File

@ -19,18 +19,6 @@ public interface SQLProperties
return properties; return properties;
} }
String getDriver();
String getHost();
String getPort();
String getDatabase();
String getUsername();
String getPassword();
default String toURLPlain() default String toURLPlain()
{ {
return String.format("jdbc:%s://%s:%s/%s", return String.format("jdbc:%s://%s:%s/%s",
@ -40,6 +28,14 @@ public interface SQLProperties
this.getDatabase()); this.getDatabase());
} }
String getDriver();
String getHost();
String getPort();
String getDatabase();
default String toURLWithLogin() default String toURLWithLogin()
{ {
return String.format("jdbc:%s://%s:%s/%s?user=%s&password=%s", return String.format("jdbc:%s://%s:%s/%s?user=%s&password=%s",
@ -50,4 +46,8 @@ public interface SQLProperties
this.getUsername(), this.getUsername(),
this.getPassword()); this.getPassword());
} }
String getUsername();
String getPassword();
} }

View File

@ -1,12 +1,18 @@
package me.totalfreedom.user; package me.totalfreedom.user;
import me.totalfreedom.security.perm.PermissionHolder;
import me.totalfreedom.economy.EconomicEntity; import me.totalfreedom.economy.EconomicEntity;
import me.totalfreedom.economy.EconomicEntityData; import me.totalfreedom.economy.EconomicEntityData;
import me.totalfreedom.security.perm.PermissionHolder;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
public interface User extends PermissionHolder, EconomicEntity public interface User extends PermissionHolder, EconomicEntity
{ {
@Override
default EconomicEntityData getEconomicData()
{
return getUserData();
}
// Implement a few EconomicEntity methods in the User interface // Implement a few EconomicEntity methods in the User interface
@Override @Override
default String getName() default String getName()
@ -14,12 +20,6 @@ public interface User extends PermissionHolder, EconomicEntity
return getUserData().getUsername(); return getUserData().getUsername();
} }
@Override
default EconomicEntityData getEconomicData()
{
return getUserData();
}
UserData getUserData(); UserData getUserData();
Component getDisplayName(); Component getDisplayName();

View File

@ -1,7 +1,7 @@
package me.totalfreedom.user; package me.totalfreedom.user;
import me.totalfreedom.security.perm.Group;
import me.totalfreedom.economy.EconomicEntityData; import me.totalfreedom.economy.EconomicEntityData;
import me.totalfreedom.security.perm.Group;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@ -6,27 +6,29 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* This class contains the only reference to plain text component serializer, and allows access to it via wrapper functions. * This class contains the only reference to plain text component serializer, and allows access to it via wrapper
* functions.
*/ */
public class FreedomAdventure public class FreedomAdventure
{ {
private static final PlainTextComponentSerializer PLAIN_TEXT_COMPONENT_SERIALIZER =
PlainTextComponentSerializer.plainText();
private FreedomAdventure() private FreedomAdventure()
{ {
throw new UnsupportedOperationException("Instantiation of a static utility class is not supported."); throw new UnsupportedOperationException("Instantiation of a static utility class is not supported.");
} }
private static final PlainTextComponentSerializer PLAIN_TEXT_COMPONENT_SERIALIZER = PlainTextComponentSerializer.plainText(); public static String toPlainText(final Supplier<Component> supplier)
{
return toPlainText(supplier.get());
}
public static String toPlainText(final Component component) public static String toPlainText(final Component component)
{ {
return PLAIN_TEXT_COMPONENT_SERIALIZER.serialize(component); return PLAIN_TEXT_COMPONENT_SERIALIZER.serialize(component);
} }
public static String toPlainText(final Supplier<Component> supplier)
{
return toPlainText(supplier.get());
}
public static Supplier<String> supplyPlainText(final Supplier<Component> supplier) public static Supplier<String> supplyPlainText(final Supplier<Component> supplier)
{ {
return new StringRepresentationSupplier(supplier.get()); return new StringRepresentationSupplier(supplier.get());

View File

@ -31,30 +31,6 @@ public class FreedomLogger implements Audience
this.debug = debug; this.debug = debug;
} }
/**
* This method allows you to log a message to the console.
*
* @param message The message to send.
*/
public void info(final String message)
{
logger.info(message);
}
/**
* This method allows you to log a component to the console.
*
* @param component The component to send.
* @return A plain text representation of the message
*/
public String infoComponent(final Component component)
{
final String plainText = FreedomAdventure.toPlainText(component);
logger.info(plainText);
return plainText;
}
/** /**
* This method allows you to log a message to the console, * This method allows you to log a message to the console,
* while also returning a Component that could be used to * while also returning a Component that could be used to
@ -80,6 +56,28 @@ public class FreedomLogger implements Audience
public String infoComponent(final Supplier<Component> component) public String infoComponent(final Supplier<Component> component)
{ {
return this.infoComponent(component.get()); return this.infoComponent(component.get());
} /**
* This method allows you to log a message to the console.
*
* @param message The message to send.
*/
public void info(final String message)
{
logger.info(message);
}
/**
* This method allows you to log a component to the console.
*
* @param component The component to send.
* @return A plain text representation of the message
*/
public String infoComponent(final Component component)
{
final String plainText = FreedomAdventure.toPlainText(component);
logger.info(plainText);
return plainText;
} }
/** /**
@ -116,20 +114,6 @@ public class FreedomLogger implements Audience
logger.error(message); logger.error(message);
} }
/**
* This method logs an error component to the console.
*
* @param component The message to send.
*/
public String errorComponent(final Component component)
{
final String plainText = FreedomAdventure.toPlainText(component);
logger.error(plainText);
return plainText;
}
/** /**
* This method allows you to log an exception directly to the console. * This method allows you to log an exception directly to the console.
* *
@ -170,28 +154,15 @@ public class FreedomLogger implements Audience
} }
/** /**
* This method allows you to log a debug message to the console. * This method logs an error component to the console.
* This method will only log if debug mode is enabled.
* *
* @param message The message to send. * @param component The message to send.
*/ */
public void debug(final String message) public String errorComponent(final Component component)
{
if (debug)
logger.debug(message);
}
/**
* This method allows you to log a debug component to the console.
* This method will only log if debug mode is enabled.
*
* @param component The component to send.
*/
public String debugComponent(final Component component)
{ {
final String plainText = FreedomAdventure.toPlainText(component); final String plainText = FreedomAdventure.toPlainText(component);
this.debug(plainText); logger.error(plainText);
return plainText; return plainText;
} }
@ -231,6 +202,35 @@ public class FreedomLogger implements Audience
return ""; return "";
} }
/**
* This method allows you to log a debug component to the console.
* This method will only log if debug mode is enabled.
*
* @param component The component to send.
*/
public String debugComponent(final Component component)
{
final String plainText = FreedomAdventure.toPlainText(component);
this.debug(plainText);
return plainText;
}
/**
* This method allows you to log a debug message to the console.
* This method will only log if debug mode is enabled.
*
* @param message The message to send.
*/
public void debug(final String message)
{
if (debug)
logger.debug(message);
}
@Override @Override
public void sendMessage(@NotNull final ComponentLike message) public void sendMessage(@NotNull final ComponentLike message)
@ -267,6 +267,9 @@ public class FreedomLogger implements Audience
@Override @Override
public void sendMessage(@NotNull final SignedMessage signedMessage, final ChatType.@NotNull Bound boundChatType) public void sendMessage(@NotNull final SignedMessage signedMessage, final ChatType.@NotNull Bound boundChatType)
{ {
this.info(signedMessage.message()); // TODO: We might want to investigate whether this logs the ENTIRE message, including unsigned & signed content, or only the signed part. This method was written in the assumption that it provided all content. this.info(
signedMessage.message()); // TODO: We might want to investigate whether this logs the ENTIRE message,
// including unsigned & signed content, or only the signed part. This method was written in the assumption
// that it provided all content.
} }
} }

View File

@ -9,7 +9,8 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
/** /**
* This class contains different methods to provide {@link ChatType.Bound} instances for sending messages to players in game. * This class contains different methods to provide {@link ChatType.Bound} instances for sending messages to players
* in game.
* This is now a requirement for all message requests to players due to the new chat signature system. * This is now a requirement for all message requests to players due to the new chat signature system.
* <br> * <br>
* Even though Scissors has this feature disabled, upstream (Paper) and Kyori Adventure * Even though Scissors has this feature disabled, upstream (Paper) and Kyori Adventure
@ -30,13 +31,6 @@ public final class KyoriConstants
{ {
} }
public static ChatType.Bound fromPlugin(final JavaPlugin plugin)
{
final String name = plugin.getName();
final Component component = Component.text(name, NamedTextColor.GOLD);
return type.bind(component);
}
/** /**
* Represents a Chat Bound for a plugin. * Represents a Chat Bound for a plugin.
* <br> * <br>
@ -53,6 +47,13 @@ public final class KyoriConstants
return fromPlugin(plugin); return fromPlugin(plugin);
} }
public static ChatType.Bound fromPlugin(final JavaPlugin plugin)
{
final String name = plugin.getName();
final Component component = Component.text(name, NamedTextColor.GOLD);
return type.bind(component);
}
/** /**
* Represents a Chat Bound for a player. * Represents a Chat Bound for a player.
* Chat bounds are required for sending messages to players. * Chat bounds are required for sending messages to players.
@ -73,6 +74,7 @@ public final class KyoriConstants
* <br> * <br>
* The chat bound is a representation of a validated chat signature, * The chat bound is a representation of a validated chat signature,
* which is tied directly to the user's account name. In our case, this is the player's name. * which is tied directly to the user's account name. In our case, this is the player's name.
*
* @param sender The console to get the bound for. * @param sender The console to get the bound for.
* @return A ChatType.Bound instance for the console. * @return A ChatType.Bound instance for the console.
*/ */

View File

@ -20,7 +20,8 @@ public class Shaper
this.world = world; this.world = world;
} }
public List<Location> generate(final int count, final DoubleUnaryOperator x, final DoubleUnaryOperator y, final DoubleUnaryOperator z) public List<Location> generate(final int count, final DoubleUnaryOperator x, final DoubleUnaryOperator y,
final DoubleUnaryOperator z)
{ {
final double step = (start - end) / (count - 1); final double step = (start - end) / (count - 1);
final LinkedList<Location> lset = new LinkedList<>(); final LinkedList<Location> lset = new LinkedList<>();

View File

@ -1,17 +1,27 @@
[Google GSON]: https://github.com/google/gson "Google GSON" [Google GSON]: https://github.com/google/gson "Google GSON"
[Jetbrains Annotations]: https://github.com/JetBrains/JetBrains.Annotations "JetBrains Annotations" [Jetbrains Annotations]: https://github.com/JetBrains/JetBrains.Annotations "JetBrains Annotations"
[Lombok]: https://github.com/projectlombok/lombok "Lombok" [Lombok]: https://github.com/projectlombok/lombok "Lombok"
[Apache Commons]: https://github.com/apache/commons-lang "Apache Commons" [Apache Commons]: https://github.com/apache/commons-lang "Apache Commons"
[SLF4J]: https://github.com/qos-ch/slf4j "SLF4J" [SLF4J]: https://github.com/qos-ch/slf4j "SLF4J"
[Paper]: https://github.com/PaperMC/Paper "Paper" [Paper]: https://github.com/PaperMC/Paper "Paper"
[Kyori Adventure]: https://github.com/KyoriPowered/adventure "Kyori Adventure" [Kyori Adventure]: https://github.com/KyoriPowered/adventure "Kyori Adventure"
[Reflections API]: https://github.com/ronmamo/reflections "Reflections API" [Reflections API]: https://github.com/ronmamo/reflections "Reflections API"
[TotalFreedomMod]: https://github.com/AtlasMediaGroup/TotalFreedomMod "TotalFreedomMod" [TotalFreedomMod]: https://github.com/AtlasMediaGroup/TotalFreedomMod "TotalFreedomMod"
##### #####
![Header Image](https://media.discordapp.net/attachments/436759124953399296/1107175759941996544/20230514_002037_0000.png) ![Header Image](https://media.discordapp.net/attachments/436759124953399296/1107175759941996544/20230514_002037_0000.png)
### ###
[<img src="https://img.shields.io/static/v1?label=%20&message=Help%20Wanted&color=red&style=for-the-badge">](https://discord.gg/4PdtmrVNRx) [<img src="https://img.shields.io/static/v1?label=%20&message=Help%20Wanted&color=red&style=for-the-badge">](https://discord.gg/4PdtmrVNRx)
![GitHub contributors](https://img.shields.io/github/contributors/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge) ![GitHub contributors](https://img.shields.io/github/contributors/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge)
@ -21,6 +31,7 @@
![Codacy grade](https://img.shields.io/codacy/grade/7a0fa4694878444dabc6cc2804fbf125?style=for-the-badge) ![Codacy grade](https://img.shields.io/codacy/grade/7a0fa4694878444dabc6cc2804fbf125?style=for-the-badge)
### ###
[<img src="https://img.shields.io/static/v1?label=Roadmap&message=Google%20Docs&color=4285F4&style=for-the-badge&logo=googledrive">](https://docs.google.com/document/d/197fwNo076RsCiPW6e6QWaGEzTGnDcRuf5FBA6lNeiPE) [<img src="https://img.shields.io/static/v1?label=Roadmap&message=Google%20Docs&color=4285F4&style=for-the-badge&logo=googledrive">](https://docs.google.com/document/d/197fwNo076RsCiPW6e6QWaGEzTGnDcRuf5FBA6lNeiPE)
[<img src="https://img.shields.io/github/license/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge">](https://github.com/SimplexDevelopment/FreedomNetworkSuite/blob/kitchen-sink/LICENSE.md) [<img src="https://img.shields.io/github/license/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge">](https://github.com/SimplexDevelopment/FreedomNetworkSuite/blob/kitchen-sink/LICENSE.md)
![GitHub top language](https://img.shields.io/github/languages/top/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge&logo=github) ![GitHub top language](https://img.shields.io/github/languages/top/SimplexDevelopment/FreedomNetworkSuite?style=for-the-badge&logo=github)
@ -40,16 +51,18 @@ Honorable mention:
[<img src="https://img.shields.io/static/v1?label=Plex&message=A%20New%20Freedom%20Plugin&color=4285F4&style=flat-square&logo=plex)">](https://github.com/plexusorg/Plex) [<img src="https://img.shields.io/static/v1?label=Plex&message=A%20New%20Freedom%20Plugin&color=4285F4&style=flat-square&logo=plex)">](https://github.com/plexusorg/Plex)
This proof-of-concept also uses the following libraries: This proof-of-concept also uses the following libraries:
- [Google GSON] for Json interpretation
- [Jetbrains Annotations] for additional compiler annotations - [Google GSON] for Json interpretation
- [Lombok] for boilerplate generation - [Jetbrains Annotations] for additional compiler annotations
- [Apache Commons] for various utilities - [Lombok] for boilerplate generation
- [SLF4J] for logging - [Apache Commons] for various utilities
- [Paper] for the server implementation - [SLF4J] for logging
- [Kyori Adventure] for chat formatting - [Paper] for the server implementation
- [Reflections API] for reflections - [Kyori Adventure] for chat formatting
- [Reflections API] for reflections
# Developers # Developers
[<img src="https://img.shields.io/static/v1?label=Developer&message=Patches&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/Paldiu) [<img src="https://img.shields.io/static/v1?label=Developer&message=Patches&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/Paldiu)
<br /> <br />
[<img src="https://img.shields.io/static/v1?label=Developer&message=Video&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/VideoGameSmash12) [<img src="https://img.shields.io/static/v1?label=Developer&message=Video&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/VideoGameSmash12)
@ -57,7 +70,9 @@ This proof-of-concept also uses the following libraries:
[<img src="https://img.shields.io/static/v1?label=Developer&message=Allink&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/allinkdev) [<img src="https://img.shields.io/static/v1?label=Developer&message=Allink&color=blueviolet&style=for-the-badge&logo=intellijidea">](https://github.com/allinkdev)
# To Do List # To Do List
Patchwork: Patchwork:
- [x] Logging System - [x] Logging System
- [x] SQL API - [x] SQL API
- [x] Economy API - [x] Economy API
@ -72,6 +87,7 @@ Patchwork:
- [ ] Event API - [ ] Event API
Datura: Datura:
- [ ] Permission Handling - [ ] Permission Handling
- [ ] Permission Registration & Assignment - [ ] Permission Registration & Assignment
- [ ] SQL Data Handling - [ ] SQL Data Handling
@ -81,6 +97,7 @@ Datura:
- [ ] Punishment Systems (e.x. Locker, Halter, Muter, Cager) - [ ] Punishment Systems (e.x. Locker, Halter, Muter, Cager)
Fossil: Fossil:
- [x] Economy Implementation - [x] Economy Implementation
- [ ] Particle Implementation / Trails - [ ] Particle Implementation / Trails
- [ ] Command Implementations - [ ] Command Implementations
@ -89,6 +106,7 @@ Fossil:
- [ ] Jumppads - [ ] Jumppads
Corvo: Corvo:
- [ ] Service Implementation - [ ] Service Implementation
- [ ] Service Handling - [ ] Service Handling
- [ ] Task Implementation - [ ] Task Implementation