diff --git a/Patchwork/build.gradle b/Patchwork/build.gradle
index 6ce2389..fbf0c03 100644
--- a/Patchwork/build.gradle
+++ b/Patchwork/build.gradle
@@ -8,6 +8,9 @@ repositories {
dependencies {
library 'io.projectreactor:reactor-core:3.5.4'
library 'io.github.classgraph:classgraph:4.8.162'
+ library 'com.electronwill.night-config:core:3.6.7'
+ library 'com.electronwill.night-config:toml:3.6.7'
+ library 'com.electronwill.night-config:json:3.6.7'
api 'org.slf4j:slf4j-api:1.7.36'
testImplementation platform('org.junit:junit-bom:5.9.1')
@@ -19,7 +22,7 @@ bukkit {
description = "Freedom Network Suite Core Module (API & Library)"
}
-var weight = 1
+def weight = 1
test {
useJUnitPlatform()
diff --git a/Patchwork/src/main/java/fns/patchwork/base/Registration.java b/Patchwork/src/main/java/fns/patchwork/base/Registration.java
index ff81f7f..db491fa 100644
--- a/Patchwork/src/main/java/fns/patchwork/base/Registration.java
+++ b/Patchwork/src/main/java/fns/patchwork/base/Registration.java
@@ -23,12 +23,12 @@
package fns.patchwork.base;
-import fns.patchwork.data.ConfigRegistry;
-import fns.patchwork.data.EventRegistry;
-import fns.patchwork.data.GroupRegistry;
-import fns.patchwork.data.ModuleRegistry;
-import fns.patchwork.data.ServiceTaskRegistry;
-import fns.patchwork.data.UserRegistry;
+import fns.patchwork.registry.ConfigRegistry;
+import fns.patchwork.registry.EventRegistry;
+import fns.patchwork.registry.GroupRegistry;
+import fns.patchwork.registry.ModuleRegistry;
+import fns.patchwork.registry.ServiceTaskRegistry;
+import fns.patchwork.registry.UserRegistry;
/**
* This class is a holder for each registry in the data package.
diff --git a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
index 8e8c1fa..01162c6 100644
--- a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
+++ b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
@@ -43,6 +43,7 @@ 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.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@@ -60,6 +61,8 @@ import org.jetbrains.annotations.NotNull;
*
* This class is not meant to be used outside Patchwork.
*/
+@ApiStatus.Internal
+@ApiStatus.NonExtendable
public final class BukkitDelegate extends Command implements PluginIdentifiableCommand
{
private final JavaPlugin plugin;
@@ -274,3 +277,4 @@ public final class BukkitDelegate extends Command implements PluginIdentifiableC
return this.plugin;
}
}
+
diff --git a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
index 54f2a30..20be942 100644
--- a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
+++ b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
@@ -27,6 +27,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.jetbrains.annotations.ApiStatus;
/**
* A marker interface which represents a holder for multiple {@link Completion} annotations.
@@ -36,6 +37,7 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
+@ApiStatus.Internal
public @interface Completions
{
/**
diff --git a/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java
new file mode 100644
index 0000000..1a77e45
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.config;
+
+import com.electronwill.nightconfig.core.Config;
+import com.electronwill.nightconfig.core.ConfigFormat;
+import com.electronwill.nightconfig.core.io.ConfigParser;
+import com.electronwill.nightconfig.core.io.ConfigWriter;
+import com.electronwill.nightconfig.json.FancyJsonWriter;
+import com.electronwill.nightconfig.json.JsonFormat;
+import com.electronwill.nightconfig.json.JsonParser;
+import com.electronwill.nightconfig.json.MinimalJsonWriter;
+import com.electronwill.nightconfig.toml.TomlFormat;
+import com.electronwill.nightconfig.toml.TomlParser;
+import com.electronwill.nightconfig.toml.TomlWriter;
+
+public enum ConfigType
+{
+ TOML(TomlFormat.instance(),
+ ".toml",
+ new TomlWriter(),
+ new TomlParser()),
+ JSON(JsonFormat.minimalInstance(),
+ ".json",
+ new MinimalJsonWriter(),
+ new JsonParser()),
+ JSON_FANCY(JsonFormat.fancyInstance(),
+ ".json",
+ new FancyJsonWriter(),
+ new JsonParser());
+
+ private final ConfigFormat> format;
+ private final String fileExtension;
+ private final ConfigWriter writer;
+ private final ConfigParser> parser;
+
+ ConfigType(final ConfigFormat> format,
+ final String fileExtension,
+ final ConfigWriter writer,
+ final ConfigParser> parser)
+ {
+ this.format = format;
+ this.fileExtension = fileExtension;
+ this.writer = writer;
+ this.parser = parser;
+ }
+
+ public ConfigFormat> getFormat()
+ {
+ return this.format;
+ }
+
+ public String getExtension()
+ {
+ return this.fileExtension;
+ }
+
+ public ConfigWriter getWriter()
+ {
+ return this.writer;
+ }
+
+ public ConfigParser> getParser()
+ {
+ return this.parser;
+ }
+}
diff --git a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java
index 3dfeec4..b7b4780 100644
--- a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java
+++ b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java
@@ -23,9 +23,8 @@
package fns.patchwork.config;
-import fns.patchwork.api.Context;
+import fns.patchwork.provider.Context;
import fns.patchwork.provider.ContextProvider;
-import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.io.File;
diff --git a/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java
new file mode 100644
index 0000000..410e791
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java
@@ -0,0 +1,224 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.config;
+
+import com.electronwill.nightconfig.core.Config;
+import com.electronwill.nightconfig.core.ConfigFormat;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+import fns.patchwork.utils.FileUtils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+
+public final class GenericConfiguration implements Configuration
+{
+ private final File configFile;
+ private final String fileName;
+ private final Config config;
+ private final ConfigType configType;
+
+ public GenericConfiguration(@NotNull final ConfigType configType,
+ @Nullable final JavaPlugin plugin,
+ @NotNull final File dataFolder,
+ @NotNull final String fileName,
+ final boolean isConcurrent) throws IOException
+ {
+ if (!fileName.endsWith(configType.getExtension()))
+ throw new IllegalArgumentException("File name must end with " + configType.getExtension() + "!");
+
+ // Ternary just to piss off Allink :)
+ final Optional file = (plugin != null) ?
+ FileUtils.getOrCreateFileWithResource(dataFolder, fileName, plugin) :
+ FileUtils.getOrCreateFile(dataFolder, fileName);
+
+ if (file.isEmpty())
+ throw new FileNotFoundException();
+
+ this.configFile = file.get();
+ this.fileName = fileName;
+ this.configType = configType;
+
+ final ConfigFormat> format = configType.getFormat();
+
+ // Another ternary just to piss off Allink :)
+ this.config = isConcurrent ? format.createConcurrentConfig() : format.createConfig();
+
+ this.load();
+ }
+
+ public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName)
+ throws IOException
+ {
+ this(type, null, dataFolder, fileName, false);
+ }
+
+ public GenericConfiguration(final ConfigType type, final JavaPlugin plugin, final String fileName)
+ throws IOException
+ {
+ this(type, plugin, plugin.getDataFolder(), fileName, false);
+ }
+
+ public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName,
+ final boolean isConcurrent)
+ throws IOException
+ {
+ this(type, null, dataFolder, fileName, isConcurrent);
+ }
+
+ @Override
+ public void save() throws IOException
+ {
+ final File backup = new File(this.configFile.getParentFile(), this.fileName + ".bak");
+
+ if (backup.exists())
+ Files.delete(backup.toPath());
+
+ Files.copy(this.configFile.toPath(), backup.toPath());
+
+ try (final FileWriter writer = new FileWriter(this.configFile))
+ {
+ this.configType.getWriter().write(this.getConfig(), writer);
+ }
+ }
+
+ @Override
+ public void load() throws IOException {
+ try (final FileReader reader = new FileReader(this.configFile)) {
+ this.config.clear();
+
+ final UnmodifiableConfig parsed = this.configType.getParser().parse(reader).unmodifiable();
+ this.config.putAll(parsed);
+ }
+ }
+
+ @Override
+ public String getFileName()
+ {
+ return fileName;
+ }
+
+ @Override
+ public File getConfigurationFile()
+ {
+ return configFile;
+ }
+
+ @Override
+ public String getString(final String path)
+ {
+ if (!(this.getConfig().get(path) instanceof String))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a string!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ public boolean getBoolean(String path)
+ {
+ if (!(this.getConfig().get(path) instanceof Boolean))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a boolean!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ @ApiStatus.Internal
+ public @Unmodifiable List getList(String path, Class clazz)
+ {
+ // TODO: Figure out how to parse lists with Night Config.
+
+ return new ArrayList<>();
+ }
+
+ @Override
+ @ApiStatus.Internal
+ public @Unmodifiable List getStringList(String path)
+ {
+ // TODO: Figure out how to parse lists with Night Config.
+
+ return new ArrayList<>();
+ }
+
+ @Override
+ public int getInt(String path)
+ {
+ return this.getConfig().getInt(path);
+ }
+
+ @Override
+ public long getLong(String path)
+ {
+ return this.getConfig().getLong(path);
+ }
+
+ @Override
+ public double getDouble(String path)
+ {
+ if (!(this.getConfig().get(path) instanceof Double))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a double!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ public Optional get(String path, Class clazz)
+ {
+ // I love ternary statements, sorry Allink :)
+ return clazz.isInstance(this.getConfig().get(path)) ?
+ Optional.of(clazz.cast(this.getConfig().get(path))) :
+ Optional.empty();
+ }
+
+ @Override
+ public T getOrDefault(String path, Class clazz, T fallback)
+ {
+ return this.get(path, clazz).orElse(fallback);
+ }
+
+ @Override
+ public void set(final String path, final T value) {
+ this.config.set(path, value);
+ }
+
+ private UnmodifiableConfig getConfig()
+ {
+ return config.unmodifiable();
+ }
+
+ public ConfigType getConfigType()
+ {
+ return configType;
+ }
+}
diff --git a/Patchwork/src/main/java/fns/patchwork/event/EventBus.java b/Patchwork/src/main/java/fns/patchwork/event/EventBus.java
index 9325702..cbda58d 100644
--- a/Patchwork/src/main/java/fns/patchwork/event/EventBus.java
+++ b/Patchwork/src/main/java/fns/patchwork/event/EventBus.java
@@ -23,7 +23,7 @@
package fns.patchwork.event;
-import fns.patchwork.api.Context;
+import fns.patchwork.provider.Context;
import fns.patchwork.base.Patchwork;
import fns.patchwork.service.Service;
import java.util.HashSet;
diff --git a/Patchwork/src/main/java/fns/patchwork/particle/Trail.java b/Patchwork/src/main/java/fns/patchwork/particle/Trail.java
index 9d9f70b..62ceff3 100644
--- a/Patchwork/src/main/java/fns/patchwork/particle/Trail.java
+++ b/Patchwork/src/main/java/fns/patchwork/particle/Trail.java
@@ -23,7 +23,6 @@
package fns.patchwork.particle;
-import fns.patchwork.api.Interpolator;
import fns.patchwork.utils.InterpolationUtils;
import java.util.Set;
import java.util.UUID;
@@ -101,7 +100,7 @@ public interface Trail
* @see #getColor()
* @see Particle
* @see InterpolationUtils
- * @see Interpolator
+ * @see InterpolationUtils.Interpolator
*/
@Nullable
Set getColors();
diff --git a/Patchwork/src/main/java/fns/patchwork/api/Context.java b/Patchwork/src/main/java/fns/patchwork/provider/Context.java
similarity index 99%
rename from Patchwork/src/main/java/fns/patchwork/api/Context.java
rename to Patchwork/src/main/java/fns/patchwork/provider/Context.java
index 3369673..06757d0 100644
--- a/Patchwork/src/main/java/fns/patchwork/api/Context.java
+++ b/Patchwork/src/main/java/fns/patchwork/provider/Context.java
@@ -21,9 +21,8 @@
* SOFTWARE.
*/
-package fns.patchwork.api;
+package fns.patchwork.provider;
-import fns.patchwork.provider.ContextProvider;
import java.util.function.Function;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java
similarity index 98%
rename from Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java
index da6f64a..d2aab53 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.config.Configuration;
import java.util.HashMap;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java
similarity index 98%
rename from Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java
index 72ef4cb..443c61f 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.event.FEvent;
import fns.patchwork.provider.EventProvider;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java
similarity index 98%
rename from Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java
index aaf3ab5..a2f5c1e 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.permissible.Group;
import java.util.ArrayList;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java
similarity index 98%
rename from Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java
index c4b9f95..69873b6 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.provider.ModuleProvider;
import java.util.ArrayList;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java
similarity index 99%
rename from Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java
index b9b7b5a..c346c0c 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.service.Service;
import fns.patchwork.service.ServiceSubscription;
diff --git a/Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java
similarity index 99%
rename from Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java
rename to Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java
index 10435e8..7daaefe 100644
--- a/Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java
+++ b/Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.data;
+package fns.patchwork.registry;
import fns.patchwork.user.User;
import fns.patchwork.user.UserData;
diff --git a/Patchwork/src/main/java/fns/patchwork/api/Serializable.java b/Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java
similarity index 98%
rename from Patchwork/src/main/java/fns/patchwork/api/Serializable.java
rename to Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java
index a8d33b3..cee964f 100644
--- a/Patchwork/src/main/java/fns/patchwork/api/Serializable.java
+++ b/Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-package fns.patchwork.api;
+package fns.patchwork.serializer;
/**
* This interface represents a Serializable object. Objects which require custom serialization and cannot simply
diff --git a/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java
new file mode 100644
index 0000000..0f62b70
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.utils;
+
+import fns.patchwork.utils.logging.FNS4J;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public final class FileUtils
+{
+ @NonNls
+ private static final String CREATED_DIRECTORY = "Created directory ";
+
+ private FileUtils()
+ {
+ throw new AssertionError();
+ }
+
+ public static String getExtension(@NotNull final File file)
+ {
+ return file.getName()
+ .substring(file.getName()
+ .lastIndexOf('.'));
+ }
+
+ public static Optional getOrCreateDirectory(final File parentDirectory, final String directoryName)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File directory = new File(parentDirectory, directoryName);
+ if (directory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directory.getAbsolutePath());
+
+ if (directory.exists())
+ return Optional.of(directory);
+
+ return Optional.empty();
+ }
+
+ public static Optional getOrCreateDirectory(final Path directoryPath)
+ {
+ Optional directory = Optional.empty();
+
+ if (directoryPath.toFile().mkdirs())
+ directory = Optional.of(directoryPath.toFile());
+
+ if (directory.isPresent())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directoryPath.toAbsolutePath());
+
+ return directory;
+ }
+
+ public static Optional getOrCreateFile(final File parentDirectory, final String fileName)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File file = new File(parentDirectory, fileName);
+ try
+ {
+ if (file.createNewFile())
+ FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath());
+
+ return Optional.of(file);
+ }
+ catch (final IOException ex)
+ {
+ FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage());
+ return Optional.empty();
+ }
+ }
+
+ public static Optional getOrCreateFileWithResource(final File parentDirectory,
+ final String fileName,
+ final JavaPlugin plugin)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File file = new File(parentDirectory, fileName);
+ try
+ {
+ if (file.createNewFile())
+ {
+ FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath());
+ FNS4J.PATCHWORK.info(
+ "Copying default file from resources/" + fileName + " to " + file.getAbsolutePath());
+
+ plugin.saveResource(fileName, true);
+ FNS4J.PATCHWORK.info(
+ "Successfully copied default file from resources/" + fileName + " to " + file.getAbsolutePath());
+ }
+
+ return Optional.of(file);
+ }
+ catch (final IOException ex)
+ {
+ FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage());
+ return Optional.empty();
+ }
+ }
+}
diff --git a/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java b/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java
index c48ef15..7c7e46e 100644
--- a/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java
+++ b/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java
@@ -23,7 +23,6 @@
package fns.patchwork.utils;
-import fns.patchwork.api.Interpolator;
import java.util.LinkedHashSet;
import java.util.Set;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -155,4 +154,24 @@ public final class InterpolationUtils
{
return componentRGBGradient(length, from, to, InterpolationUtils::linear);
}
+
+ /**
+ * Interpolates a range of values and returns the results in a {@link Double} array.
+ *
+ * This is a functional interface, to allow for lambda expressions, but also for anonymous custom interpolation
+ * implementations.
+ */
+ @FunctionalInterface
+ public static interface Interpolator
+ {
+ /**
+ * Interpolates a range of values and returns the results in a {@link Double} array.
+ *
+ * @param from The starting value.
+ * @param to The ending value.
+ * @param max The number of values to interpolate.
+ * @return The interpolated values.
+ */
+ double[] interpolate(final double from, final double to, final int max);
+ }
}
diff --git a/Tyr/build.gradle b/Tyr/build.gradle
index 8e258d1..4bd615f 100644
--- a/Tyr/build.gradle
+++ b/Tyr/build.gradle
@@ -9,6 +9,12 @@ repositories {
mavenCentral()
}
+bukkit {
+ main = "fns.tyr.Tyr"
+ description = "SSH -> RCON Module for Freedom Network Suite"
+ depend = ["Patchwork", "Datura"]
+}
+
dependencies {
compileOnly project(":Patchwork")
compileOnly project(":Datura")
diff --git a/Veritas/build.gradle b/Veritas/build.gradle
index 777e8e8..afed021 100644
--- a/Veritas/build.gradle
+++ b/Veritas/build.gradle
@@ -9,6 +9,12 @@ repositories {
mavenCentral()
}
+bukkit {
+ main = "fns.veritas.Veritas"
+ description = "Discord Module for Freedom Network Suite"
+ depend = ["Patchwork", "Datura"]
+}
+
dependencies {
compileOnly project(":Patchwork")
compileOnly project(":Datura")
diff --git a/Veritas/src/main/java/fns/veritas/Aggregate.java b/Veritas/src/main/java/fns/veritas/Aggregate.java
index 3d2df51..51afc09 100644
--- a/Veritas/src/main/java/fns/veritas/Aggregate.java
+++ b/Veritas/src/main/java/fns/veritas/Aggregate.java
@@ -24,31 +24,53 @@
package fns.veritas;
import fns.patchwork.utils.logging.FNS4J;
+import fns.veritas.bukkit.BukkitNative;
+import fns.veritas.bukkit.ServerListener;
import fns.veritas.client.BotClient;
import fns.veritas.client.BotConfig;
+import org.bukkit.Bukkit;
public class Aggregate
{
- private final FNS4J logger;
+ private static final FNS4J logger = FNS4J.getLogger("Veritas");
private final BotClient bot;
private final Veritas plugin;
+ private final BukkitNative bukkitNativeListener;
+ private final ServerListener serverListener;
public Aggregate(final Veritas plugin)
{
this.plugin = plugin;
- this.logger = FNS4J.getLogger(plugin.getName());
this.bot = new BotClient(new BotConfig(plugin));
+ this.bukkitNativeListener = new BukkitNative(plugin);
+ this.serverListener = new ServerListener(plugin);
+
+ Bukkit.getServer().getPluginManager().registerEvents(this.getBukkitNativeListener(), plugin);
+ this.getServerListener().minecraftChatBound().subscribe();
}
- public FNS4J getLogger() {
+ public static FNS4J getLogger()
+ {
return logger;
}
- public BotClient getBot() {
+ public ServerListener getServerListener()
+ {
+ return serverListener;
+ }
+
+ public BukkitNative getBukkitNativeListener()
+ {
+ return bukkitNativeListener;
+ }
+
+ public BotClient getBot()
+ {
return bot;
}
- public Veritas getPlugin() {
+ public Veritas getPlugin()
+ {
return plugin;
}
}
diff --git a/Veritas/src/main/java/fns/veritas/Veritas.java b/Veritas/src/main/java/fns/veritas/Veritas.java
index bd705e2..4616533 100644
--- a/Veritas/src/main/java/fns/veritas/Veritas.java
+++ b/Veritas/src/main/java/fns/veritas/Veritas.java
@@ -34,9 +34,8 @@ public class Veritas extends JavaPlugin
{
this.aggregate = new Aggregate(this);
- getAggregate()
- .getLogger()
- .info("Veritas has been enabled!");
+ Aggregate.getLogger()
+ .info("Veritas has been enabled!");
}
public Aggregate getAggregate()
diff --git a/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java b/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java
index 287ce04..6d598d7 100644
--- a/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java
+++ b/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java
@@ -109,7 +109,7 @@ public class BukkitNative implements Listener
if (!plugin.getServer().hasWhitelist() && bot != null)
{
plugin.getAggregate().getBot().messageChatChannel(player.getName()
- + " \u00BB "
+ + " ยป "
+ message, true);
}
}
diff --git a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java
index 1c9b2f3..6d9e113 100644
--- a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java
+++ b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java
@@ -27,6 +27,7 @@ import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.entity.Attachment;
import discord4j.core.object.entity.Member;
import discord4j.core.object.entity.Message;
+import fns.veritas.Aggregate;
import fns.veritas.Veritas;
import fns.veritas.client.BotClient;
import net.kyori.adventure.text.Component;
@@ -35,6 +36,8 @@ import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+import reactor.core.publisher.Mono;
public class ServerListener
{
@@ -48,9 +51,9 @@ public class ServerListener
this.bot = plugin.getAggregate().getBot();
}
- public void minecraftChatBound()
+ public Mono minecraftChatBound()
{
- bot.getClient()
+ return bot.getClient()
.getEventDispatcher()
.on(MessageCreateEvent.class)
.filter(m -> m.getMessage()
@@ -62,8 +65,9 @@ public class ServerListener
.orElseThrow(IllegalAccessError::new)
.getId()
.equals(plugin.getAggregate().getBot().getClient().getSelfId()))
- .doOnError(plugin.getAggregate().getLogger()::error)
- .subscribe(this::doMessageBodyDetails);
+ .doOnError(Aggregate.getLogger()::error)
+ .doOnNext(this::doMessageBodyDetails)
+ .then();
}
private void doMessageBodyDetails(MessageCreateEvent m)
@@ -83,6 +87,14 @@ public class ServerListener
user = user.append(Component.text(member.getDisplayName().trim()));
+ final TextComponent message = builder(msg);
+
+ Bukkit.broadcast(builder.append(prefix, user, message).build());
+ }
+
+ @NotNull
+ private TextComponent builder(Message msg)
+ {
TextComponent message = Component.text(": ", NamedTextColor.DARK_GRAY)
.append(
Component.text(msg.getContent(), NamedTextColor.WHITE));
@@ -102,7 +114,6 @@ public class ServerListener
.clickEvent(ClickEvent.openUrl(attachment.getUrl())));
}
}
-
- Bukkit.broadcast(builder.append(prefix, user, message).build());
+ return message;
}
}
diff --git a/Veritas/src/main/java/fns/veritas/client/BotClient.java b/Veritas/src/main/java/fns/veritas/client/BotClient.java
index 6de4540..46bc391 100644
--- a/Veritas/src/main/java/fns/veritas/client/BotClient.java
+++ b/Veritas/src/main/java/fns/veritas/client/BotClient.java
@@ -23,36 +23,40 @@
package fns.veritas.client;
-import com.google.common.collect.ImmutableList;
import discord4j.common.util.Snowflake;
import discord4j.core.DiscordClientBuilder;
import discord4j.core.GatewayDiscordClient;
+import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
import discord4j.core.object.entity.Guild;
import discord4j.core.object.entity.Message;
import discord4j.core.object.entity.channel.TextChannel;
import discord4j.core.spec.MessageCreateSpec;
+import fns.veritas.cmd.base.BotCommandHandler;
+import java.util.List;
import reactor.core.publisher.Mono;
public class BotClient
{
private final GatewayDiscordClient client;
private final BotConfig config;
- private final ImmutableList DISCORD_SUBDOMAINS;
+ private final List subdomains;
public BotClient(final BotConfig config)
{
this.config = config;
- this.DISCORD_SUBDOMAINS = ImmutableList.of("discordapp.com", "discord.com", "discord.gg");
+ this.subdomains = List.of("discordapp.com", "discord.com", "discord.gg");
+
this.client = DiscordClientBuilder.create(config.getToken())
.build()
.login()
.block();
- }
- public void validateConnection()
- {
if (client == null)
throw new IllegalStateException();
+
+ final BotCommandHandler handler = new BotCommandHandler(client.getRestClient());
+
+ client.on(ChatInputInteractionEvent.class, handler::handle);
}
public String getBotId()
@@ -87,14 +91,14 @@ public class BotClient
public void messageChatChannel(String message, boolean system)
{
- String chat_channel_id = config.getChatChannelId().asString();
+ String channelID = config.getChatChannelId().asString();
String sanitizedMessage = (system) ? message : sanitizeChatMessage(message);
if (sanitizedMessage.isBlank())
return;
- if (!chat_channel_id.isEmpty())
+ if (!channelID.isEmpty())
{
MessageCreateSpec spec = MessageCreateSpec.builder()
.content(sanitizedMessage)
@@ -124,7 +128,7 @@ public class BotClient
return "";
}
- for (String subdomain : DISCORD_SUBDOMAINS)
+ for (String subdomain : subdomains)
{
if (message.toLowerCase().contains(subdomain + "/invite"))
{
diff --git a/Veritas/src/main/java/fns/veritas/client/BotConfig.java b/Veritas/src/main/java/fns/veritas/client/BotConfig.java
index 4ab90f0..08407ad 100644
--- a/Veritas/src/main/java/fns/veritas/client/BotConfig.java
+++ b/Veritas/src/main/java/fns/veritas/client/BotConfig.java
@@ -24,8 +24,8 @@
package fns.veritas.client;
import discord4j.common.util.Snowflake;
-import discord4j.discordjson.Id;
import fns.patchwork.config.WrappedBukkitConfiguration;
+import fns.veritas.Aggregate;
import fns.veritas.Veritas;
import java.io.File;
import java.io.IOException;
@@ -108,7 +108,7 @@ public class BotConfig
}
catch (IOException e)
{
- plugin.getAggregate().getLogger().error(e);
+ Aggregate.getLogger().error(e);
}
}
}
diff --git a/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java b/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java
new file mode 100644
index 0000000..b3cc27a
--- /dev/null
+++ b/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite
+ * Copyright (C) 2023 Total Freedom Server Network and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.veritas.cmd;
+
+import discord4j.core.event.domain.interaction.ChatInputInteractionEvent;
+import fns.veritas.cmd.base.BotCommand;
+import fns.veritas.messaging.Embed;
+import fns.veritas.messaging.EmbedWrapper;
+import java.util.ArrayList;
+import java.util.List;
+import reactor.core.publisher.Mono;
+
+public class HelpCommand implements BotCommand
+{
+ @Override
+ public String getName()
+ {
+ return "help";
+ }
+
+ @Override
+ public Mono handle(ChatInputInteractionEvent event)
+ {
+ final List