Datura - Data Manager

This commit is contained in:
Paul Reilly 2023-05-12 23:30:08 -05:00
parent 98d7ffafe3
commit 90c5f2a6f8
15 changed files with 961 additions and 12 deletions

View File

@ -9,6 +9,7 @@ import org.bukkit.plugin.java.JavaPlugin;
public class CommonsBase extends JavaPlugin public class CommonsBase extends JavaPlugin
{ {
private final EventBus eventBus = new EventBus(this); private final EventBus eventBus = new EventBus(this);
private final Registration registration = new Registration();
public static CommonsBase getInstance() public static CommonsBase getInstance()
{ {
@ -34,4 +35,8 @@ public class CommonsBase extends JavaPlugin
{ {
return Bukkit.getServicesManager().getRegistration(EventBus.class); return Bukkit.getServicesManager().getRegistration(EventBus.class);
} }
public Registration getRegistrations() {
return registration;
}
} }

View File

@ -1,9 +1,6 @@
package me.totalfreedom.base; package me.totalfreedom.base;
import me.totalfreedom.data.CommandRegistry; import me.totalfreedom.data.*;
import me.totalfreedom.data.EventRegistry;
import me.totalfreedom.data.ServiceRegistry;
import me.totalfreedom.data.UserRegistry;
public class Registration public class Registration
{ {
@ -11,6 +8,7 @@ public class Registration
private final EventRegistry eventRegistry; private final EventRegistry eventRegistry;
private final UserRegistry userRegistry; private final UserRegistry userRegistry;
private final ServiceRegistry serviceRegistry; private final ServiceRegistry serviceRegistry;
private final ModuleRegistry moduleRegistry;
public Registration() public Registration()
{ {
@ -18,6 +16,12 @@ public class Registration
this.eventRegistry = new EventRegistry(); this.eventRegistry = new EventRegistry();
this.userRegistry = new UserRegistry(); this.userRegistry = new UserRegistry();
this.serviceRegistry = new ServiceRegistry(); this.serviceRegistry = new ServiceRegistry();
this.moduleRegistry = new ModuleRegistry();
}
public ModuleRegistry getModuleRegistry()
{
return moduleRegistry;
} }
public CommandRegistry getCommandRegistry() public CommandRegistry getCommandRegistry()

View File

@ -7,17 +7,17 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
public interface Node public interface Node
{ {
String getKey(); String key();
boolean getValue(); boolean value();
Permission bukkit(); Permission bukkit();
NodeType getType(); NodeType type();
boolean compare(Node node); boolean compare(Node node);
long getExpiry(); long expiry();
boolean isExpired(); boolean isExpired();
@ -25,7 +25,7 @@ public interface Node
boolean isTemporary(); boolean isTemporary();
boolean isWildcard(); boolean wildcard();
boolean isNegated(); boolean negated();
} }

View File

@ -1,6 +1,7 @@
package me.totalfreedom.sql; package me.totalfreedom.sql;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -8,11 +9,13 @@ public interface SQL
{ {
CompletableFuture<Connection> getConnection(String url); CompletableFuture<Connection> getConnection(String url);
CompletableFuture<PreparedStatement> prepareStatement(String query, Object... args);
CompletableFuture<ResultSet> executeQuery(String query, Object... args); CompletableFuture<ResultSet> executeQuery(String query, Object... args);
CompletableFuture<Boolean> executeUpdate(String query, Object... args); CompletableFuture<Integer> executeUpdate(String query, Object... args);
CompletableFuture<Void> execute(String query, Object... args); CompletableFuture<Boolean> execute(String query, Object... args);
CompletableFuture<Boolean> createTable(String table, String... columns); CompletableFuture<Boolean> createTable(String table, String... columns);
} }

View File

@ -0,0 +1,131 @@
package me.totalfreedom.utils;
import net.kyori.adventure.text.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Supplier;
public class FreedomLogger
{
private final Logger logger;
private boolean debug = false;
private FreedomLogger(String moduleName)
{
this.logger = LoggerFactory.getLogger("FreedomNetworkSuite::" + moduleName);
}
public static FreedomLogger getLogger(String moduleName)
{
return new FreedomLogger(moduleName);
}
public void setDebugMode(boolean debug)
{
this.debug = debug;
}
/**
* This method allows you to log a message to the console.
*
* @param message The message to send.
*/
public void info(String message)
{
logger.info(message);
}
/**
* This method allows you to log a message to the console,
* while also returning a Component that could be used to
* message a player.
*
* @param message The message to send.
* @return A component representation of the message.
*/
public Component info(Supplier<String> message)
{
logger.info(message.get());
return Component.text(message.get());
}
/**
* This method allows you to log a warning to the console.
*
* @param message The message to send.
*/
public void warn(String message)
{
logger.warn(message);
}
/**
* This method logs an error message to the console.
* It is highly recommended to deconstruct the stack trace and pass it
* in a more readable format to this method.
*
* @param message The message to send.
*/
public void error(String message)
{
logger.error(message);
}
/**
* This method allows you to log an exception directly to the console.
*
* @param th The exception to log.
*/
public void error(Throwable th)
{
logger.error("An error occurred:\n", th);
}
/**
* This method allows you to log an error message to the console,
* while also returning a Component that could be used to
* message a player. It is highly recommended that you deconstruct and limit
* the stack trace before passing it to this method, if you are intending to
* use it for player communication.
*
* @param message The message to send.
* @return A component representation of the message.
*/
public Component error(Supplier<String> message)
{
logger.error(message.get());
return Component.text(message.get());
}
/**
* 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(String message)
{
if (debug)
logger.debug(message);
}
/**
* This method allows you to log a debug message to the console,
* while also returning a Component that could be used to
* message a player. This method will only log if debug mode is enabled.
* If debug mode is not enabled, this method will return an empty component.
*
* @param message The message to send.
* @return A component representation of the message.
*/
public Component debug(Supplier<String> message)
{
if (debug)
{
logger.debug(message.get());
return Component.text(message.get());
}
return Component.empty();
}
}

42
Datura/.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

21
Datura/build.gradle Normal file
View File

@ -0,0 +1,21 @@
plugins {
id 'java'
}
group = 'me.totalfreedom'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
compileOnly project(":Commons")
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}

View File

@ -0,0 +1,16 @@
package me.totalfreedom.datura;
import me.totalfreedom.base.CommonsBase;
import org.bukkit.plugin.java.JavaPlugin;
public class Datura extends JavaPlugin
{
@Override
public void onEnable()
{
CommonsBase.getInstance()
.getRegistrations()
.getModuleRegistry()
.addModule(this);
}
}

View File

@ -0,0 +1,43 @@
package me.totalfreedom.datura.perms;
import me.totalfreedom.security.Node;
import me.totalfreedom.security.NodeType;
public class DefaultNodes
{
private DefaultNodes() {
throw new AssertionError();
}
public static final Node OP = new PermissionNodeBuilder()
.key("freedom.master_key")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(true)
.build();
public static final Node NON_OP = new PermissionNodeBuilder()
.key("freedom.default")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(false)
.build();
public static final Node ALL = new PermissionNodeBuilder()
.key("*")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(true)
.build();
public static final Node NONE = new PermissionNodeBuilder()
.key("freedom.none")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(false)
.build();
}

View File

@ -0,0 +1,240 @@
package me.totalfreedom.datura.perms;
import me.totalfreedom.base.CommonsBase;
import me.totalfreedom.security.Group;
import me.totalfreedom.security.Node;
import net.kyori.adventure.text.Component;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
public class FreedomGroup implements Group
{
private final Component name;
private final Component prefix;
private final Component abbreviation;
private final int weight;
private final boolean isDefault;
private final boolean isHidden;
private final Set<Node> permissions;
private final PermissionAttachment attachment;
public FreedomGroup(Component name,
Component prefix,
Component abbreviation,
int weight,
boolean isDefault,
boolean isHidden)
{
this.name = name;
this.prefix = prefix;
this.abbreviation = abbreviation;
this.weight = weight;
this.isDefault = isDefault;
this.isHidden = isHidden;
this.permissions = new HashSet<>();
this.attachment = new PermissionAttachment(CommonsBase.getInstance(), this);
}
@Override
public Component getName()
{
return name;
}
@Override
public Component getPrefix()
{
return prefix;
}
@Override
public Component getAbbreviation()
{
return abbreviation;
}
@Override
public int getWeight()
{
return weight;
}
@Override
public boolean isDefault()
{
return isDefault;
}
@Override
public boolean isHidden()
{
return isHidden;
}
@Override
public UUID getUniqueId()
{
return UUID.nameUUIDFromBytes(getName().toString().getBytes());
}
@Override
public Set<Node> permissions()
{
return permissions;
}
@Override
public boolean addPermission(Node node)
{
return permissions().add(node);
}
@Override
public boolean removePermission(Node node)
{
return permissions().remove(node);
}
@Override
public boolean isPermissionSet(@NotNull String name)
{
Node node = permissions().stream()
.filter(n -> n.key().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
return node != null && node.value();
}
@Override
public boolean isPermissionSet(@NotNull Permission perm)
{
Node node = permissions()
.stream()
.filter(n -> n.bukkit().equals(perm))
.findFirst()
.orElse(null);
return node != null && node.value();
}
@Override
public boolean hasPermission(@NotNull String name)
{
Node node = permissions().stream()
.filter(n -> n.key().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
return node != null && node.value();
}
@Override
public boolean hasPermission(@NotNull Permission perm)
{
Node node = permissions()
.stream()
.filter(n -> n.bukkit().equals(perm))
.findFirst()
.orElse(null);
return node != null && node.value();
}
/**
* Adds a permission to the relative PermissionAttachment for this group.
* This method is not thread-safe and should not be called asynchronously.
* <p>
* This method is only here for compatibility with the Bukkit API.
*
* @param plugin The plugin responsible for this attachment. May not be null
* or disabled.
* @param name Name of the permission to attach
* @param value Value of the permission
* @return This group's PermissionAttachment.
*/
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value)
{
attachment.setPermission(name, value);
return attachment;
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin)
{
return new PermissionAttachment(plugin, this);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks)
{
attachment.setPermission(name, value);
return attachment;
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks)
{
return new PermissionAttachment(plugin, this);
}
@Override
public void removeAttachment(@NotNull PermissionAttachment attachment)
{
// This method shouldn't do anything, because we don't want to remove our attachment.
}
@Override
public void recalculatePermissions()
{
// Not sure what this method should do, so I'm leaving it empty.
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions()
{
return permissions()
.stream()
.map(n -> new PermissionAttachmentInfo(
this,
n.key(),
attachment,
n.value()))
.collect(Collectors.toSet());
}
@Override
public boolean isOp()
{
Node node = permissions()
.stream()
.filter(n -> n.equals(DefaultNodes.OP))
.findFirst()
.orElse(null);
return node != null && node.value();
}
@Override
public void setOp(boolean value)
{
if (value)
{
permissions().add(DefaultNodes.OP);
} else
{
permissions().remove(DefaultNodes.OP);
}
}
}

View File

@ -0,0 +1,206 @@
package me.totalfreedom.datura.perms;
import me.totalfreedom.base.CommonsBase;
import me.totalfreedom.security.Node;
import me.totalfreedom.user.User;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* The superinterface User extends PermissionHolder,
* which is an extension of {@link org.bukkit.permissions.Permissible}.
* This means that our permission data can be interchanged with other permission plugins.
*/
public class FreedomUser implements User
{
private final UUID uuid;
private final Set<Node> permissions;
private final Map<Node, PermissionAttachment> bukkitAttachments = new HashMap<>();
private final Component displayName;
private final String NOT_ONLINE = "Player is not online";
public FreedomUser(Player player)
{
this.uuid = player.getUniqueId();
this.permissions = new HashSet<>();
this.displayName = player.displayName();
}
@Override
public UUID getUniqueId()
{
return uuid;
}
@Override
public Set<Node> permissions()
{
return permissions;
}
@Override
public boolean addPermission(Node node)
{
PermissionAttachment attachment = addAttachment(CommonsBase.getInstance(), node.key(), node.value());
bukkitAttachments.put(node, attachment);
return permissions().add(node);
}
@Override
public boolean removePermission(Node node)
{
removeAttachment(bukkitAttachments.get(node));
bukkitAttachments.remove(node);
return permissions.remove(node);
}
@Override
public Component getDisplayName()
{
return displayName;
}
@Override
public boolean isOnline()
{
return Bukkit.getPlayer(uuid) != null;
}
@Override
public boolean isPermissionSet(@NotNull String name)
{
Player player = Bukkit.getPlayer(uuid);
return player != null && player.isPermissionSet(name);
}
@Override
public boolean isPermissionSet(@NotNull Permission perm)
{
Player player = Bukkit.getPlayer(uuid);
return player != null && player.isPermissionSet(perm);
}
@Override
public boolean hasPermission(@NotNull String name)
{
Player player = Bukkit.getPlayer(uuid);
return player != null && player.hasPermission(name);
}
@Override
public boolean hasPermission(@NotNull Permission perm)
{
Player player = Bukkit.getPlayer(uuid);
return player != null && player.hasPermission(perm);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value)
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, name, value);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin)
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks)
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, name, value, ticks);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks)
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, ticks);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public void removeAttachment(@NotNull PermissionAttachment attachment)
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
player.removeAttachment(attachment);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public void recalculatePermissions()
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
player.recalculatePermissions();
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions()
{
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.getEffectivePermissions();
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public boolean isOp()
{
return permissions().contains(DefaultNodes.OP);
}
@Override
public void setOp(boolean value)
{
if (value) {
permissions().add(DefaultNodes.OP);
} else {
permissions().remove(DefaultNodes.OP);
}
}
}

View File

@ -0,0 +1,53 @@
package me.totalfreedom.datura.perms;
import me.totalfreedom.security.Node;
import me.totalfreedom.security.NodeType;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
record PermissionNode(String key,
boolean value,
long expiry,
NodeType type,
boolean wildcard,
boolean negated) implements Node
{
@Override
public Permission bukkit()
{
return new Permission(key(),
value() ? PermissionDefault.TRUE : PermissionDefault.FALSE);
}
@Override
public boolean compare(Node node)
{
return node.key().equalsIgnoreCase(key())
&& node.value() == value()
&& node.type() == type();
}
@Override
public boolean isExpired()
{
if (isPermanent())
{
return false;
}
return System.currentTimeMillis() > expiry();
}
@Override
public boolean isPermanent()
{
return expiry() == -1;
}
@Override
public boolean isTemporary()
{
return !isPermanent();
}
}

View File

@ -0,0 +1,63 @@
package me.totalfreedom.datura.perms;
import me.totalfreedom.security.Node;
import me.totalfreedom.security.NodeBuilder;
import me.totalfreedom.security.NodeType;
public class PermissionNodeBuilder implements NodeBuilder
{
private String key = "freedom.default";
private boolean value = true;
private long expiry = -1;
private NodeType type = NodeType.PERMISSION;
private boolean wildcard = false;
private boolean negated = false;
@Override
public NodeBuilder key(String key)
{
this.key = key;
return this;
}
@Override
public NodeBuilder value(boolean value)
{
this.value = value;
return this;
}
@Override
public NodeBuilder expiry(long expiry)
{
this.expiry = expiry;
return this;
}
@Override
public NodeBuilder type(NodeType type)
{
this.type = type;
return this;
}
@Override
public NodeBuilder wildcard(boolean wildcard)
{
this.wildcard = wildcard;
return this;
}
@Override
public NodeBuilder negated(boolean negated)
{
this.negated = negated;
return this;
}
@Override
public Node build()
{
return new PermissionNode(key, value, expiry, type, wildcard, negated);
}
}

View File

@ -0,0 +1,121 @@
package me.totalfreedom.datura.sql;
import me.totalfreedom.sql.SQL;
import java.sql.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class MySQL implements SQL
{
private String url = "jdbc:mysql://";
public MySQL(String host, int port, String database) {
url += host + ":" + port + "/" + database;
}
/**
* Adds credentials to the MySQL URL.
* If the URL already contains credentials, they will be overwritten.
*
* @param username The username to add
* @param password The password to add
*/
public void addCredentials(String username, String password) {
if (url.contains("?user=")) {
url = url.split("\\x3f")[0];
}
url += "?user=" + username + "&password=" + password;
}
@Override
public CompletableFuture<Connection> getConnection(String url)
{
return CompletableFuture.supplyAsync(() -> {
try {
return DriverManager.getConnection(url);
} catch (SQLException ex) {
throw new CompletionException("Failed to connect to the database: "
+ url + "\n", ex);
}
});
}
@Override
public CompletableFuture<PreparedStatement> prepareStatement(String query, Object... args)
{
return getConnection(url)
.thenApplyAsync(connection -> {
try {
PreparedStatement statement = connection.prepareStatement(query);
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
return statement;
} catch (SQLException ex) {
throw new CompletionException("Failed to prepare statement: "
+ query + "\n", ex);
}
});
}
@Override
public CompletableFuture<ResultSet> executeQuery(String query, Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement -> {
try {
return statement.executeQuery();
} catch (SQLException ex) {
throw new CompletionException("Failed to retrieve a result set from query: "
+ query + "\n", ex);
}
});
}
@Override
public CompletableFuture<Integer> executeUpdate(String query, Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement -> {
try {
return statement.executeUpdate();
} catch (SQLException ex) {
throw new CompletionException("Failed to execute update: "
+ query + "\n", ex);
}
});
}
@Override
public CompletableFuture<Boolean> execute(String query, Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement -> {
try {
return statement.execute();
} catch (SQLException ex) {
throw new CompletionException("Failed to execute statement: "
+ query + "\n", ex);
}
});
}
@Override
public CompletableFuture<Boolean> createTable(String table, String... columns)
{
StringBuilder query = new StringBuilder();
query.append("CREATE TABLE IF NOT EXISTS ? (");
for (int i = 0; i < columns.length; i++) {
query.append("?");
if (i != columns.length - 1) {
query.append(", ");
}
}
query.append(")");
return execute(query.toString(), table, columns);
}
}

View File

@ -1,3 +1,4 @@
rootProject.name = 'FreedomNetworkSuite' rootProject.name = 'FreedomNetworkSuite'
include 'Commons' include 'Commons'
include 'Datura'