This commit is contained in:
Paul Reilly
2023-05-15 01:30:37 -05:00
parent 5a395554cf
commit 6f400e505c
57 changed files with 699 additions and 10 deletions

42
Patchwork/.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
Patchwork/build.gradle Normal file
View File

@ -0,0 +1,21 @@
group = 'me.totalfreedom'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
api 'io.projectreactor:reactor-core:3.5.4'
api 'org.reflections:reflections:0.10.2'
api 'org.slf4j:slf4j-api:1.7.36'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
var weight = 1
test {
useJUnitPlatform()
}

View File

@ -0,0 +1,179 @@
package me.totalfreedom.api;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.block.Action;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface Context<T>
{
T get();
default @Nullable String asString()
{
if (get() instanceof String string)
{
return string;
} else
{
return null;
}
}
default @Nullable Boolean asBoolean()
{
if (get() instanceof Boolean bool)
{
return bool;
} else
{
return null;
}
}
default @Nullable Double asDouble()
{
if (get() instanceof Double doub)
{
return doub;
} else
{
return null;
}
}
default @Nullable Integer asInt() {
if (get() instanceof Integer integer) {
return integer;
} else {
return null;
}
}
default @Nullable Long asLong() {
if (get() instanceof Long longg) {
return longg;
} else {
return null;
}
}
default @Nullable Float asFloat() {
if (get() instanceof Float floatt) {
return floatt;
} else {
return null;
}
}
default @Nullable Player asPlayer()
{
if (get() instanceof Player player)
{
return player;
} else
{
return null;
}
}
default @Nullable CommandSender asCommandSender()
{
if (get() instanceof CommandSender commandSender)
{
return commandSender;
} else
{
return null;
}
}
default @NotNull String literal()
{
return get().toString();
}
default @Nullable World asWorld()
{
if (get() instanceof World world)
{
return world;
} else
{
return null;
}
}
default @Nullable Location asLocation()
{
if (get() instanceof Location location)
{
return location;
} else
{
return null;
}
}
default @Nullable LivingEntity asLivingEntity()
{
if (get() instanceof LivingEntity livingEntity)
{
return livingEntity;
} else
{
return null;
}
}
default @Nullable Component asComponent()
{
if (get() instanceof Component component)
{
return component;
} else
{
return null;
}
}
default @Nullable Projectile asProjectile()
{
if (get() instanceof Projectile projectile)
{
return projectile;
} else
{
return null;
}
}
default @Nullable Action asAction()
{
if (get() instanceof Action action)
{
return action;
} else
{
return null;
}
}
default <U> @Nullable U asCustom(Class<U> clazz)
{
if (clazz.isInstance(get()))
{
return clazz.cast(get());
} else
{
return null;
}
}
}

View File

@ -0,0 +1,15 @@
package me.totalfreedom.api;
public interface Serializable<T>
{
/**
* Serialize an object to a string.
* Ideally, this should serialize to an SQL query for easy data transfer.
*
* @param object The object to serialize
* @return The serialized object
*/
String serialize(T object);
T deserialize(Context<?>... contexts);
}

View File

@ -0,0 +1,44 @@
package me.totalfreedom.base;
import me.totalfreedom.event.EventBus;
import me.totalfreedom.service.FreedomExecutor;
import org.bukkit.plugin.java.JavaPlugin;
public class CommonsBase extends JavaPlugin
{
private final EventBus eventBus = new EventBus(this);
private final Registration registration = new Registration();
private final FreedomExecutor executor = new FreedomExecutor();
public static CommonsBase getInstance()
{
return JavaPlugin.getPlugin(CommonsBase.class);
}
@Override
public void onEnable()
{
getRegistrations().getServiceRegistry().register(this, eventBus);
getExecutor().getSync()
.execute(() -> getRegistrations()
.getServiceRegistry()
.startAll());
}
@Override
public void onDisable()
{
getRegistrations().getServiceRegistry().stopAll();
getRegistrations().getServiceRegistry().unregister(EventBus.class, eventBus);
}
public Registration getRegistrations()
{
return registration;
}
public FreedomExecutor getExecutor()
{
return executor;
}
}

View File

@ -0,0 +1,53 @@
package me.totalfreedom.base;
import me.totalfreedom.data.*;
public class Registration
{
private final CommandRegistry commandRegistry;
private final EventRegistry eventRegistry;
private final UserRegistry userRegistry;
private final ServiceRegistry serviceRegistry;
private final ModuleRegistry moduleRegistry;
private final GroupRegistry groupRegistry;
public Registration()
{
this.commandRegistry = new CommandRegistry();
this.eventRegistry = new EventRegistry();
this.userRegistry = new UserRegistry();
this.serviceRegistry = new ServiceRegistry();
this.moduleRegistry = new ModuleRegistry();
this.groupRegistry = new GroupRegistry();
}
public ModuleRegistry getModuleRegistry()
{
return moduleRegistry;
}
public CommandRegistry getCommandRegistry()
{
return commandRegistry;
}
public EventRegistry getEventRegistry()
{
return eventRegistry;
}
public UserRegistry getUserRegistry()
{
return userRegistry;
}
public ServiceRegistry getServiceRegistry()
{
return serviceRegistry;
}
public GroupRegistry getGroupRegistry()
{
return groupRegistry;
}
}

View File

@ -0,0 +1,120 @@
package me.totalfreedom.command;
import jdk.jshell.MethodSnippet;
import me.totalfreedom.api.Context;
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.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
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.plugin = plugin;
this.command = command;
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 CommandSender sender, @NotNull String commandLabel, @NotNull 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)
{
ContextProvider provider = new ContextProvider();
Set<Subcommand> nodes = command.getSubcommands().keySet();
for (Subcommand node : nodes) {
Class<?>[] argTypes = node.args();
if (argTypes.length != args.length)
continue;
Object[] objects = new Object[0];
for (int i = 0; i < argTypes.length; i++) {
Class<?> argType = argTypes[i];
String arg = args[i];
if (argType == String.class)
continue;
Context<?> context = () -> provider.fromString(arg);
if (!argType.isInstance(context.get())) {
throw new IllegalStateException();
}
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().getValue().invoke(command, sender);
} catch (Exception ex)
{
FreedomLogger.getLogger("Patchwork")
.error(ex);
}
}
return true;
}
@Override
public @NotNull Plugin getPlugin()
{
return this.plugin;
}
}

View File

@ -0,0 +1,72 @@
package me.totalfreedom.command;
import me.totalfreedom.command.annotation.Base;
import me.totalfreedom.command.annotation.Info;
import me.totalfreedom.command.annotation.Permissive;
import me.totalfreedom.command.annotation.Subcommand;
import me.totalfreedom.utils.Pair;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
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 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<>();
if (this.getClass().isAnnotationPresent(Base.class))
{
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;
}
Stream.of(this.getClass().getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(Subcommand.class))
.forEach(method -> this.subcommands.put(method.getDeclaredAnnotation(Subcommand.class), method));
}
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;
}
}

View File

@ -0,0 +1,25 @@
package me.totalfreedom.command;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
public class CommandHandler
{
private final JavaPlugin plugin;
public CommandHandler(JavaPlugin plugin)
{
this.plugin = plugin;
}
// TODO: Figure out how to use CommandExecutor and TabCompleter.
// We need to find a way to resolve PluginCommands so we can
// set the executor and tab completer as necessary.
// OR we need to find an alternative way to process tab completions.
public <T extends CommandBase> void registerCommand(T command) {
BukkitDelegator delegate = new BukkitDelegator(plugin, command);
Bukkit.getCommandMap().register(plugin.getName(), delegate);
}
}

View File

@ -0,0 +1,13 @@
package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This annotation is used to mark a method as the command's default method.
* This is the method that will be run to execute the command when a user inputs /{command}
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Base
{
}

View File

@ -0,0 +1,16 @@
package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Info
{
String name();
String description() default "This is the default command description.";
String usage() default "/<command>";
String[] aliases() default {};
}

View File

@ -0,0 +1,14 @@
package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Permissive
{
String perm();
boolean onlyPlayers() default false;
String noPerms() default "You do not have permission to use this command.";
}

View File

@ -0,0 +1,14 @@
package me.totalfreedom.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Subcommand
{
String name() default "";
String permission();
Class<?>[] args() default {};
}

View File

@ -0,0 +1,40 @@
package me.totalfreedom.config;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.List;
public interface Configuration
{
YamlConfiguration asYaml();
void save() throws IOException;
void load() throws IOException;
String getFileName();
File getConfigurationFile();
String getString(String path);
Boolean getBoolean(String path);
<T> List<T> getList(String path);
List<String> getStringList(String path);
Integer getInt(String path);
Long getLong(String path);
Double getDouble(String path);
<T> void set(String path, T value);
<T> T get(String path, Class<T> type);
<T> T getOrDefault(String path, Class<T> type, T fallback);
}

View File

@ -0,0 +1,61 @@
package me.totalfreedom.data;
import co.aikar.commands.*;
import me.totalfreedom.base.CommonsBase;
public class CommandRegistry
{
private final PaperCommandManager manager;
private final PaperCommandContexts contexts;
private final PaperCommandCompletions completions;
private final CommandReplacements replacements;
private final CommandConditions<BukkitCommandIssuer,
BukkitCommandExecutionContext,
BukkitConditionContext> conditions;
public CommandRegistry()
{
this.manager = new PaperCommandManager(CommonsBase.getInstance());
this.contexts = new PaperCommandContexts(manager);
this.completions = new PaperCommandCompletions(manager);
this.replacements = manager.getCommandReplacements();
this.conditions = manager.getCommandConditions();
}
public PaperCommandManager getManager()
{
return manager;
}
public PaperCommandContexts getContexts()
{
return contexts;
}
public PaperCommandCompletions getCompletions()
{
return completions;
}
public CommandReplacements getReplacements()
{
return replacements;
}
public CommandConditions<BukkitCommandIssuer,
BukkitCommandExecutionContext,
BukkitConditionContext> getConditions()
{
return conditions;
}
public void register(BaseCommand cmd)
{
manager.registerCommand(cmd);
}
public void unregister(BaseCommand cmd)
{
manager.unregisterCommand(cmd);
}
}

View File

@ -0,0 +1,11 @@
package me.totalfreedom.data;
import me.totalfreedom.config.Configuration;
import java.util.HashSet;
import java.util.Set;
public class ConfigRegistry
{
Set<Configuration> configurationSet = new HashSet<>();
}

View File

@ -0,0 +1,39 @@
package me.totalfreedom.data;
import me.totalfreedom.event.FEvent;
import me.totalfreedom.provider.EventProvider;
import java.util.ArrayList;
import java.util.List;
public class EventRegistry
{
private final List<FEvent> events;
public EventRegistry()
{
this.events = new ArrayList<>();
}
public void register(final FEvent event)
{
this.events.add(event);
}
public void unregister(final FEvent event)
{
this.events.remove(event);
}
public <T extends FEvent> EventProvider<T> getEvent(final Class<T> clazz)
{
for (final FEvent event : this.events)
{
if (clazz.isInstance(event))
{
return () -> (T) event;
}
}
return null;
}
}

View File

@ -0,0 +1,40 @@
package me.totalfreedom.data;
import me.totalfreedom.security.Group;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import java.util.ArrayList;
import java.util.List;
public class GroupRegistry
{
private final List<Group> groups;
public GroupRegistry()
{
this.groups = new ArrayList<>();
}
public boolean registerGroup(Group group) {
return groups.add(group);
}
public boolean unregisterGroup(Group group) {
return groups.remove(group);
}
public Group getGroup(String name) {
PlainTextComponentSerializer s = PlainTextComponentSerializer.plainText();
for (Group group : groups) {
String n = s.serialize(group.getName());
if (n.equalsIgnoreCase(name)) {
return group;
}
}
return null;
}
public List<Group> getGroups() {
return groups;
}
}

View File

@ -0,0 +1,44 @@
package me.totalfreedom.data;
import me.totalfreedom.provider.ModuleProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.ArrayList;
import java.util.List;
public class ModuleRegistry
{
private final List<JavaPlugin> plugins;
public ModuleRegistry()
{
this.plugins = new ArrayList<>();
}
public void addModule(final JavaPlugin plugin)
{
if (this.plugins.contains(plugin))
{
return;
}
this.plugins.add(plugin);
}
public void removeModule(final JavaPlugin plugin) {
this.plugins.remove(plugin);
}
@SuppressWarnings("unchecked")
public <T extends JavaPlugin> ModuleProvider<T> getModule(Class<T> clazz)
{
for (JavaPlugin plugin : plugins)
{
if (clazz.isInstance(plugin))
{
return () -> (T) plugin;
}
}
return () -> null;
}
}

View File

@ -0,0 +1,70 @@
package me.totalfreedom.data;
import me.totalfreedom.service.Service;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import java.util.ArrayList;
import java.util.List;
public class ServiceRegistry
{
private final List<Service> services;
public ServiceRegistry()
{
this.services = new ArrayList<>();
}
public void startAll()
{
for (Service service : this.services)
{
service.start();
}
}
public void stopAll()
{
for (Service service : this.services)
{
service.stop();
}
}
@SuppressWarnings("unchecked")
// Suppressing is fine here; we know that the service is of type T extends Service,
// and calling getClass() on this object would effectively be Class<T>, though we may lose
// the identity of the code signature in the process.
// In this case, that is fine.
public <T extends Service> void register(Plugin plugin, final T service)
{
this.services.add(service);
if (!service.getClass().isInstance(service))
{
throw new UnknownError("""
A critical issue has been encountered:
The service %s is not an instance of itself.
This is a critical issue and should be reported immediately.
""".formatted(service.getClass().getName()));
}
Bukkit.getServicesManager().register(
(Class<T>) service.getClass(),
service,
plugin,
ServicePriority.Normal);
}
public <T extends Service> RegisteredServiceProvider<T> getService(Class<T> clazz)
{
return Bukkit.getServicesManager().getRegistration(clazz);
}
public void unregister(Class<? extends Service> clazz, Service service)
{
this.services.remove(service);
Bukkit.getServicesManager().unregister(clazz, service);
}
}

View File

@ -0,0 +1,37 @@
package me.totalfreedom.data;
import me.totalfreedom.user.User;
import me.totalfreedom.user.UserData;
import java.util.HashMap;
import java.util.Map;
public class UserRegistry
{
private final Map<User, UserData> userDataMap;
public UserRegistry()
{
this.userDataMap = new HashMap<>();
}
public UserData getUserData(User user)
{
return userDataMap.get(user);
}
public void registerUserData(User user, UserData userData)
{
userDataMap.put(user, userData);
}
public void unregisterUserData(User user)
{
userDataMap.remove(user);
}
public Map<User, UserData> getUserDataMap()
{
return userDataMap;
}
}

View File

@ -0,0 +1,7 @@
package me.totalfreedom.event;
@FunctionalInterface
public interface Callback<T extends FEvent>
{
void call(T event);
}

View File

@ -0,0 +1,58 @@
package me.totalfreedom.event;
import me.totalfreedom.api.Context;
import me.totalfreedom.base.CommonsBase;
import me.totalfreedom.service.Service;
import java.util.HashSet;
import java.util.Set;
public class EventBus extends Service
{
private final CommonsBase plugin;
private final Set<FEvent> eventSet = new HashSet<>();
private final SubscriptionBox<?> runningSubscriptions = new SubscriptionBox<>();
public EventBus(CommonsBase plugin)
{
super("event_bus");
this.plugin = plugin;
}
public void addEvent(FEvent event)
{
eventSet.add(event);
}
public <T extends FEvent> EventSubscription<T> subscribe(Class<T> eventClass, Callback<T> callback)
{
Context<T> eventContext = () -> eventSet.stream()
.filter(event -> event.getEventClass().equals(eventClass))
.findFirst()
.map(eventClass::cast)
.orElse(null);
if (eventContext.get() == null)
{
throw new IllegalArgumentException("Event class " + eventClass.getName() + " is not registered.");
}
return new EventSubscription<>(eventContext.get(), callback);
}
public void unsubscribe(EventSubscription<?> subscription)
{
runningSubscriptions.removeSubscription(subscription);
}
public CommonsBase getCommonsBase()
{
return plugin;
}
@Override
public void tick()
{
runningSubscriptions.tick();
}
}

View File

@ -0,0 +1,36 @@
package me.totalfreedom.event;
import me.totalfreedom.api.Context;
import java.util.function.Supplier;
public final class EventSubscription<T extends FEvent>
{
private final T event;
private final Callback<T> callback;
public EventSubscription(T event, Callback<T> callback)
{
this.event = event;
this.callback = callback;
}
public T getEvent()
{
return event;
}
public boolean cancel()
{
return getEvent().cancel();
}
public boolean isCancelled()
{
return getEvent().isCancelled();
}
public Callback<T> getCallback() {
return callback;
}
}

View File

@ -0,0 +1,28 @@
package me.totalfreedom.event;
import me.totalfreedom.api.Context;
public abstract class FEvent
{
private boolean isCancelled;
protected FEvent()
{
this.isCancelled = false;
}
public abstract void call(Callback<FEvent> callback);
public boolean cancel()
{
this.isCancelled = true;
return isCancelled();
}
public boolean isCancelled()
{
return isCancelled;
}
public abstract Class<? extends FEvent> getEventClass();
}

View File

@ -0,0 +1,25 @@
package me.totalfreedom.event;
import java.util.ArrayList;
import java.util.List;
class SubscriptionBox<T extends FEvent>
{
private final List<EventSubscription<T>> subscriptions;
public SubscriptionBox() {
this.subscriptions = new ArrayList<>();
}
public void addSubscription(EventSubscription<T> subscription) {
subscriptions.add(subscription);
}
public void removeSubscription(EventSubscription<?> subscription) {
subscriptions.remove(subscription);
}
public void tick() {
subscriptions.forEach(s -> s.getCallback().call(s.getEvent()));
}
}

View File

@ -0,0 +1,120 @@
package me.totalfreedom.provider;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.stream.Stream;
public class ContextProvider
{
public Object fromString(String string)
{
return Stream.of(toBoolean(string),
toDouble(string),
toInt(string),
toLong(string),
toFloat(string),
toPlayer(string),
toWorld(string),
toLocation(string),
toCommandSender(string),
toComponent(string))
.filter(Objects::nonNull)
.findFirst()
.orElse(string);
}
private @Nullable Boolean toBoolean(String string)
{
try
{
return Boolean.parseBoolean(string);
} catch (Exception e)
{
return null;
}
}
private @Nullable Double toDouble(String string)
{
try
{
return Double.parseDouble(string);
} catch (Exception e)
{
return null;
}
}
private @Nullable Integer toInt(String string)
{
try
{
return Integer.parseInt(string);
} catch (Exception e)
{
return null;
}
}
private @Nullable Long toLong(String string)
{
try
{
return Long.parseLong(string);
} catch (Exception e)
{
return null;
}
}
private @Nullable Float toFloat(String string)
{
try
{
return Float.parseFloat(string);
} catch (Exception e)
{
return null;
}
}
private @Nullable Player toPlayer(String string)
{
return Bukkit.getPlayer(string);
}
private @Nullable CommandSender toCommandSender(String string)
{
if (toPlayer(string) == null) return null;
return toPlayer(string);
}
private @Nullable World toWorld(String string)
{
return Bukkit.getWorld(string);
}
private @Nullable Location toLocation(String string)
{
String[] split = string.split(",");
if (split.length != 4 || toWorld(split[0]) == null) return null;
if (toDouble(split[1]) == null
|| toDouble(split[2]) == null
|| toDouble(split[3]) == null) return null;
return new Location(toWorld(split[0]), toDouble(split[1]), toDouble(split[2]), toDouble(split[3]));
}
private @Nullable Component toComponent(String string)
{
return Component.text(string);
}
}

View File

@ -0,0 +1,9 @@
package me.totalfreedom.provider;
import me.totalfreedom.event.FEvent;
@FunctionalInterface
public interface EventProvider<T extends FEvent>
{
T getEvent();
}

View File

@ -0,0 +1,9 @@
package me.totalfreedom.provider;
import org.bukkit.plugin.java.JavaPlugin;
@FunctionalInterface
public interface ModuleProvider<T extends JavaPlugin>
{
T getModule();
}

View File

@ -0,0 +1,8 @@
package me.totalfreedom.provider;
import me.totalfreedom.service.Service;
public interface ServiceProvider<T extends Service>
{
T getService();
}

View File

@ -0,0 +1,18 @@
package me.totalfreedom.security;
import net.kyori.adventure.text.Component;
public interface Group extends PermissionHolder
{
Component getName();
Component getPrefix();
Component getAbbreviation();
int getWeight();
boolean isDefault();
boolean isHidden();
}

View File

@ -0,0 +1,31 @@
package me.totalfreedom.security;
import org.bukkit.permissions.Permission;
import javax.annotation.concurrent.Immutable;
@Immutable
public interface Node
{
String key();
boolean value();
Permission bukkit();
NodeType type();
boolean compare(Node node);
long expiry();
boolean isExpired();
boolean isPermanent();
boolean isTemporary();
boolean wildcard();
boolean negated();
}

View File

@ -0,0 +1,18 @@
package me.totalfreedom.security;
public interface NodeBuilder
{
NodeBuilder key(String key);
NodeBuilder value(boolean value);
NodeBuilder expiry(long expiry);
NodeBuilder type(NodeType type);
NodeBuilder wildcard(boolean wildcard);
NodeBuilder negated(boolean negated);
Node build();
}

View File

@ -0,0 +1,10 @@
package me.totalfreedom.security;
public enum NodeType
{
INHERITANCE,
PREFIX,
SUFFIX,
PERMISSION,
WEIGHT
}

View File

@ -0,0 +1,18 @@
package me.totalfreedom.security;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import java.util.Set;
import java.util.UUID;
public interface PermissionHolder extends Permissible
{
UUID getUniqueId();
Set<Node> permissions();
boolean addPermission(Node node);
boolean removePermission(Node node);
}

View File

@ -0,0 +1,58 @@
package me.totalfreedom.service;
import me.totalfreedom.base.CommonsBase;
import org.bukkit.Bukkit;
import java.util.concurrent.Executor;
public class FreedomExecutor
{
private final Executor syncExecutor;
private final Executor asyncExecutor;
public FreedomExecutor()
{
syncExecutor = r -> Bukkit.getScheduler().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(long delay, long period)
{
return r -> Bukkit.getScheduler()
.runTaskTimer(
CommonsBase.getInstance(),
r,
delay,
period);
}
public Executor scheduledAsync(long delay, long period)
{
return r -> Bukkit.getScheduler()
.runTaskTimerAsynchronously(
CommonsBase.getInstance(),
r,
delay,
period);
}
public void runSync(Task task)
{
getSync().execute(task);
}
public void runAsync(Task task)
{
getAsync().execute(task);
}
}

View File

@ -0,0 +1,47 @@
package me.totalfreedom.service;
import me.totalfreedom.base.CommonsBase;
public abstract class Service
{
private final String name;
private boolean isActive = false;
protected Service(String name)
{
this.name = name;
}
public void start()
{
isActive = true;
CommonsBase.getInstance()
.getExecutor()
.getSync()
.execute(() ->
{
while (isActive)
{
tick();
}
});
}
public void stop()
{
isActive = false;
}
public abstract void tick();
public String getName()
{
return name;
}
public boolean isActive()
{
return isActive;
}
}

View File

@ -0,0 +1,24 @@
package me.totalfreedom.service;
public interface Task extends Runnable
{
void start();
void stop();
boolean isRunning();
String getName();
boolean isRepeating();
void setRepeating(long interval);
boolean isDelayed();
void setDelayed(long delay);
long getInterval();
long getDelay();
}

View File

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

View File

@ -0,0 +1,53 @@
package me.totalfreedom.sql;
import java.io.File;
import java.util.Properties;
public interface SQLProperties
{
Properties getProperties(File propertiesFile);
default Properties getDefaultProperties()
{
Properties properties = new Properties();
properties.setProperty("driver", "sqlite");
properties.setProperty("host", "localhost");
properties.setProperty("port", "3306");
properties.setProperty("database", "database.db");
properties.setProperty("username", "root");
properties.setProperty("password", "password");
return properties;
}
String getDriver();
String getHost();
String getPort();
String getDatabase();
String getUsername();
String getPassword();
default String toURLPlain()
{
return String.format("jdbc:%s://%s:%s/%s",
this.getDriver(),
this.getHost(),
this.getPort(),
this.getDatabase());
}
default String toURLWithLogin()
{
return String.format("jdbc:%s://%s:%s/%s?user=%s&password=%s",
this.getDriver(),
this.getHost(),
this.getPort(),
this.getDatabase(),
this.getUsername(),
this.getPassword());
}
}

View File

@ -0,0 +1,13 @@
package me.totalfreedom.user;
import me.totalfreedom.security.PermissionHolder;
import net.kyori.adventure.text.Component;
public interface User extends PermissionHolder
{
UserData getUserData();
Component getDisplayName();
boolean isOnline();
}

View File

@ -0,0 +1,40 @@
package me.totalfreedom.user;
import me.totalfreedom.security.Group;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public interface UserData
{
@NotNull UUID getUniqueId();
String getUsername();
User getUser();
@Nullable Group getGroup();
void setGroup(@Nullable Group group);
long getPlaytime();
void setPlaytime(long playtime);
void addPlaytime(long playtime);
void resetPlaytime();
boolean isFrozen();
void setFrozen(boolean frozen);
boolean canInteract();
void setInteractionState(boolean canInteract);
boolean isCaged();
void setCaged(boolean caged);
}

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

View File

@ -0,0 +1,26 @@
package me.totalfreedom.utils;
import java.util.UUID;
public class Identity
{
private final String key;
private final UUID id;
public Identity(String key)
{
this.key = key;
this.id = UUID.nameUUIDFromBytes(key.getBytes());
}
public String getKey()
{
return key;
}
public UUID getId()
{
return id;
}
}

View File

@ -0,0 +1,22 @@
package me.totalfreedom.utils;
public class Pair<K, V>
{
private final K key;
private final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey()
{
return key;
}
public V getValue()
{
return value;
}
}

View File

@ -0,0 +1,40 @@
package me.totalfreedom.utils;
import org.bukkit.Location;
import org.bukkit.World;
import java.util.LinkedList;
import java.util.List;
import java.util.function.DoubleUnaryOperator;
public class Shaper
{
private final double start;
private final double end;
private final World world;
public Shaper(World world, double start, double end)
{
this.start = start;
this.end = end;
this.world = world;
}
public List<Location> generate(int count, DoubleUnaryOperator x, DoubleUnaryOperator y, DoubleUnaryOperator z)
{
double step = (start - end) / (count - 1);
LinkedList<Location> lset = new LinkedList<>();
for (int i = 0; i < count; i++)
{
double t = start + i * step;
double xp = x.applyAsDouble(t);
double yp = y.applyAsDouble(t);
double zp = z.applyAsDouble(t);
lset.add(new Location(world, xp, yp, zp));
}
return lset;
}
}