From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 12 Oct 2022 10:42:15 -0400 Subject: [PATCH] Leaf Config TODO - Dreeam: Add header comment, world config, Bump Night Config to 3.7.1 Co-authored-by: MrHua269 diff --git a/build.gradle.kts b/build.gradle.kts index 4ee48737f52e24f6e7148709068c568b36a736da..f7c47c49b91952bf24995332b2d7d57a277b76cf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,9 @@ val alsoShade: Configuration by configurations.creating dependencies { implementation(project(":leaf-api")) // Gale start - project setup - Depend on own API // Leaf + + implementation("com.electronwill.night-config:toml:3.6.7") // Leaf - Night config // Dreeam TODO: Bump to 3.7.1 + // Paper start implementation("org.jline:jline-terminal-jansi:3.21.0") implementation("net.minecrell:terminalconsoleappender:1.3.0") diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java index 76a1b31a55b1838dc80b796f997edaddbcaa755c..bbeb88843f210abdf1cafed11394380cfcab0e09 100644 --- a/src/main/java/net/minecraft/server/Main.java +++ b/src/main/java/net/minecraft/server/Main.java @@ -123,6 +123,8 @@ public class Main { Bootstrap.bootStrap(); Bootstrap.validate(); Util.startTimerHackThread(); + org.dreeam.leaf.config.LeafConfig.loadConfig(); // Leaf + org.dreeam.leaf.config.LeafConfig.setupLatch(); // Leaf Path path1 = Paths.get("server.properties"); DedicatedServerSettings dedicatedserversettings = new DedicatedServerSettings(optionset); // CraftBukkit - CLI argument support diff --git a/src/main/java/org/dreeam/leaf/config/ConfigInfo.java b/src/main/java/org/dreeam/leaf/config/ConfigInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..6fbc0ad44069fbb3d85d3a6a4633e8e756196dfe --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/ConfigInfo.java @@ -0,0 +1,11 @@ +package org.dreeam.leaf.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface ConfigInfo { + String baseName(); + + String comments() default ""; +} diff --git a/src/main/java/org/dreeam/leaf/config/DoNotLoad.java b/src/main/java/org/dreeam/leaf/config/DoNotLoad.java new file mode 100644 index 0000000000000000000000000000000000000000..42ce82d388336906a91547b81f6f70766f2b10f0 --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/DoNotLoad.java @@ -0,0 +1,8 @@ +package org.dreeam.leaf.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DoNotLoad { +} diff --git a/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java b/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java new file mode 100644 index 0000000000000000000000000000000000000000..f69663d0cfebcdb598ac808d06cb069572dd6052 --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java @@ -0,0 +1,21 @@ +package org.dreeam.leaf.config; + +public enum EnumConfigCategory { + + ASYNC("async"), + PERFORMANCE("performance"), + NETWORK("network"), + FIXES("fixes"), + MISC("misc"), + GAMEPLAY("gameplay"); + + private final String baseKeyName; + + EnumConfigCategory(String baseKeyName) { + this.baseKeyName = baseKeyName; + } + + public String getBaseKeyName() { + return this.baseKeyName; + } +} diff --git a/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java b/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java new file mode 100644 index 0000000000000000000000000000000000000000..f7ab1ff5f298ff1e5e16fe5396d1e9e62a55fdfb --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java @@ -0,0 +1,8 @@ +package org.dreeam.leaf.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface HotReloadUnsupported { +} diff --git a/src/main/java/org/dreeam/leaf/config/IConfigModule.java b/src/main/java/org/dreeam/leaf/config/IConfigModule.java new file mode 100644 index 0000000000000000000000000000000000000000..da1474fa0512e50bedc8dda787bf6a1214f1ce37 --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/IConfigModule.java @@ -0,0 +1,23 @@ +package org.dreeam.leaf.config; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import org.jetbrains.annotations.NotNull; + +public interface IConfigModule { + + EnumConfigCategory getCategory(); + + String getBaseName(); + + default void onLoaded(CommentedFileConfig configInstance) { + } + + default T get(String keyName, T defaultValue, @NotNull CommentedFileConfig config) { + if (!config.contains(keyName)) { + config.set(keyName, defaultValue); + return defaultValue; + } + + return config.get(keyName); + } +} diff --git a/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/src/main/java/org/dreeam/leaf/config/LeafConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..b20ad36bb89e5226d94928832fdbcce7e096d6e2 --- /dev/null +++ b/src/main/java/org/dreeam/leaf/config/LeafConfig.java @@ -0,0 +1,220 @@ +package org.dreeam.leaf.config; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; + +public class LeafConfig { + public static final Logger LOGGER = LogManager.getLogger(LeafConfig.class.getName()); + private static final File baseConfigFolder = new File("leaf_config"); + private static final File baseConfigFile = new File(baseConfigFolder, "leaf_global_config.toml"); + private static final Set allInstanced = new HashSet<>(); + private static CommentedFileConfig configFileInstance; + public static boolean alreadyInited = false; + private static MinecraftInternalPlugin NULL_PLUGIN = new MinecraftInternalPlugin(); + + public static void setupLatch() { + alreadyInited = true; + } + + public static void reload() { + dropAllInstanced(); + try { + loadConfig(); + } catch (Exception e) { + LOGGER.error(e); + } + } + + @Contract(" -> new") + public static @NotNull CompletableFuture reloadAsync() { + return new CompletableFuture<>(); + } + + public static void dropAllInstanced() { + allInstanced.clear(); + } + + public static void loadConfig() throws IOException { + baseConfigFolder.mkdirs(); + + if (!baseConfigFile.exists()) { + baseConfigFile.createNewFile(); + } + + configFileInstance = CommentedFileConfig.ofConcurrent(baseConfigFile); + + configFileInstance.load(); + configFileInstance.setComment("", """ + Leaf Config + Github Repo: https://github.com/Winds-Studio/Leaf + Discord: dreeam___ | QQ: 2682173972"""); // Leaf TODO - need to fix + + try { + instanceAllModule(); + loadAllModules(); + } catch (Exception e) { + LOGGER.error("Failed to load config modules!", e); + } + + configFileInstance.save(); + } + + private static void loadAllModules() throws IllegalAccessException { + for (IConfigModule instanced : allInstanced) { + loadForSingle(instanced); + } + } + + private static void instanceAllModule() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + for (Class clazz : getClasses("org.dreeam.leaf.config.modules")) { + if (IConfigModule.class.isAssignableFrom(clazz)) { + allInstanced.add((IConfigModule) clazz.getConstructor().newInstance()); + } + } + } + + private static void loadForSingle(@NotNull IConfigModule singleConfigModule) throws IllegalAccessException { + final EnumConfigCategory category = singleConfigModule.getCategory(); + + Field[] fields = singleConfigModule.getClass().getDeclaredFields(); + + for (Field field : fields) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) { + boolean skipLoad = field.getAnnotation(DoNotLoad.class) != null || (alreadyInited && field.getAnnotation(HotReloadUnsupported.class) != null); + ConfigInfo configInfo = field.getAnnotation(ConfigInfo.class); + + if (skipLoad || configInfo == null) { + continue; + } + + final String fullConfigKeyName = category.getBaseKeyName() + "." + singleConfigModule.getBaseName() + "." + configInfo.baseName(); + + field.setAccessible(true); + final Object currentValue = field.get(null); + + if (!configFileInstance.contains(fullConfigKeyName)) { + if (currentValue == null) { + throw new UnsupportedOperationException("Config " + singleConfigModule.getBaseName() + "tried to add an null default value!"); + } + + final String comments = configInfo.comments(); + + if (!comments.isBlank()) { + configFileInstance.setComment(fullConfigKeyName, comments); + } + + configFileInstance.add(fullConfigKeyName, currentValue); + continue; + } + + final Object actuallyValue = configFileInstance.get(fullConfigKeyName); + field.set(null, actuallyValue); + } + } + + singleConfigModule.onLoaded(configFileInstance); + } + + public static @NotNull Set> getClasses(String pack) { + Set> classes = new LinkedHashSet<>(); + String packageDirName = pack.replace('.', '/'); + Enumeration dirs; + + try { + dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); + while (dirs.hasMoreElements()) { + URL url = dirs.nextElement(); + String protocol = url.getProtocol(); + if ("file".equals(protocol)) { + String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8); + findClassesInPackageByFile(pack, filePath, classes); + } else if ("jar".equals(protocol)) { + JarFile jar; + try { + jar = ((JarURLConnection) url.openConnection()).getJarFile(); + Enumeration entries = jar.entries(); + findClassesInPackageByJar(pack, entries, packageDirName, classes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return classes; + } + + private static void findClassesInPackageByFile(String packageName, String packagePath, Set> classes) { + File dir = new File(packagePath); + + if (!dir.exists() || !dir.isDirectory()) { + return; + } + + File[] dirfiles = dir.listFiles((file) -> file.isDirectory() || file.getName().endsWith(".class")); + if (dirfiles != null) { + for (File file : dirfiles) { + if (file.isDirectory()) { + findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classes); + } else { + String className = file.getName().substring(0, file.getName().length() - 6); + try { + classes.add(Class.forName(packageName + '.' + className)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + } + } + + private static void findClassesInPackageByJar(String packageName, Enumeration entries, String packageDirName, Set> classes) { + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (name.charAt(0) == '/') { + name = name.substring(1); + } + + if (name.startsWith(packageDirName)) { + int idx = name.lastIndexOf('/'); + + if (idx != -1) { + packageName = name.substring(0, idx).replace('/', '.'); + } + + if (name.endsWith(".class") && !entry.isDirectory()) { + String className = name.substring(packageName.length() + 1, name.length() - 6); + try { + classes.add(Class.forName(packageName + '.' + className)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + } + } +}