From 3d804a0de101daddfc6fb633ef5094f8c9baf4ba Mon Sep 17 00:00:00 2001 From: UnrealValentin Date: Thu, 29 Apr 2021 18:10:37 +0200 Subject: [PATCH] Added a working JSON config for ObjectInfos of Objects Renamed Module to RegistryModule Added parent Module class Added ModuleManager which is responsible for Module handling. Added ModuleErroredException Exception for Modules which complete a call in a errored state. Added test-run/ to .gitignore for seperating debug/test runs and normal runs. Had some hot chocolate (^///^) --- .gitignore | 11 +- src/main/java/org/hmcore/HMCore.java | 10 +- src/main/java/org/hmcore/api/ModuleState.java | 23 ++++ .../exceptions/ModuleErroredException.java | 7 ++ src/main/java/org/hmcore/modules/Module.java | 114 ++++++------------ .../org/hmcore/modules/ModuleManager.java | 101 ++++++++++++++++ .../org/hmcore/modules/RegistryModule.java | 107 ++++++++++++++++ .../config/ObjectInfoConfigHandler.java | 99 +++++++++++++-- src/test/java/org/hmcore/tests/JavaTests.java | 45 ++++++- ...odule.java => JavaTestRegistryModule.java} | 30 ++++- 10 files changed, 450 insertions(+), 97 deletions(-) create mode 100644 src/main/java/org/hmcore/api/ModuleState.java create mode 100644 src/main/java/org/hmcore/api/exceptions/ModuleErroredException.java create mode 100644 src/main/java/org/hmcore/modules/ModuleManager.java create mode 100644 src/main/java/org/hmcore/modules/RegistryModule.java rename src/test/java/org/hmcore/tests/modules/impl/{JavaTestModule.java => JavaTestRegistryModule.java} (76%) diff --git a/.gitignore b/.gitignore index 56c1b13..3cc8086 100644 --- a/.gitignore +++ b/.gitignore @@ -94,15 +94,15 @@ local.properties .idea/**/libraries # Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, +# When using Gradle or Maven with auto-import, you should exclude registryModule files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. .idea/artifacts .idea/compiler.xml .idea/jarRepositories.xml -.idea/modules.xml +.idea/registryModules.xml .idea/*.iml -.idea/modules +.idea/registryModules *.iml *.ipr @@ -140,7 +140,7 @@ fabric.properties .idea/caches/build_file_checksums.ser ### Intellij Patch ### -modules.xml +registryModules.xml .idea/misc.xml # Sonarlint plugin @@ -208,4 +208,5 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/java,eclipse,intellij,gradle -run/ \ No newline at end of file +run/ +test-run/ \ No newline at end of file diff --git a/src/main/java/org/hmcore/HMCore.java b/src/main/java/org/hmcore/HMCore.java index 4c01881..b3e6dcc 100644 --- a/src/main/java/org/hmcore/HMCore.java +++ b/src/main/java/org/hmcore/HMCore.java @@ -1,15 +1,19 @@ package org.hmcore; import org.hmcore.modules.Module; +import org.hmcore.modules.ModuleManager; +import org.hmcore.modules.RegistryModule; import java.util.HashMap; public class HMCore { - public static final HashMap> modules = new HashMap<>(); + public static final HashMap modules = new HashMap<>(); - public static void callAllModuleRegistries() { - modules.forEach((name, module) -> module.registerObjects()); + public static void main(String[] args) { + ModuleManager.loadModules(); + ModuleManager.initModules(); + ModuleManager.hookModules(); } } diff --git a/src/main/java/org/hmcore/api/ModuleState.java b/src/main/java/org/hmcore/api/ModuleState.java new file mode 100644 index 0000000..b0e1866 --- /dev/null +++ b/src/main/java/org/hmcore/api/ModuleState.java @@ -0,0 +1,23 @@ +package org.hmcore.api; + +public enum ModuleState { + + QUEUED("queuing"), + LOADED("loading"), + INITIALIZED("initalizing"), + HOOKED("hooking"), + DISABLED("disabling"), + UNLOADED("unloading"), + ERRORED(""); + + public String name; + + ModuleState(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/hmcore/api/exceptions/ModuleErroredException.java b/src/main/java/org/hmcore/api/exceptions/ModuleErroredException.java new file mode 100644 index 0000000..d6727a2 --- /dev/null +++ b/src/main/java/org/hmcore/api/exceptions/ModuleErroredException.java @@ -0,0 +1,7 @@ +package org.hmcore.api.exceptions; + +public class ModuleErroredException extends Exception{ + public ModuleErroredException(String s) { + super(s); + } +} diff --git a/src/main/java/org/hmcore/modules/Module.java b/src/main/java/org/hmcore/modules/Module.java index f1e32cf..1d8e316 100644 --- a/src/main/java/org/hmcore/modules/Module.java +++ b/src/main/java/org/hmcore/modules/Module.java @@ -1,93 +1,57 @@ package org.hmcore.modules; -import org.hmcore.registration.ObjectInfo; -import org.hmcore.registration.config.ObjectInfoData; +import org.hmcore.api.ModuleState; /** - * Represents a module that manages the objects that should get registered to Hytale. - * - * @param The object that should get registered - * @param Object that contains information about the object. Like the texture. + * Represents a module with custom features */ -public abstract class Module { - - // - // For registering Objects to Module and getting them - // Made for mods to call them - // - - /** - * For checking if the Module has the module already in its list. - * - * @param name The name or registration key of the object to be checked - * @return true when the object is already registered. - */ - public abstract boolean contains(String name); - - /** - * Gets the object for the registered name. - * - * @param name The name or key for the object that should be returned. - * @return The object under the registered name or null if it doesn't exists. - */ - public abstract T get(String name); - - /** - * Registers the object to the module. - * - * @param name The name or key for the object to get registered under. - * @param object The object that should get registered. - */ - public abstract void register(String name, T object); - - // TODO: If one option added stuff the other option doesn't had, (as example ore generation info for a block) it should be automatically added. - // TODO: The admins should be able to choose a option for each feature individually. (Block behavior, ore generation, maybe force a specific texture for users, etc.) - - /** - * Adds an option for information to the object. - * There can be multiple options for information and the server administrators can choose which to use. - * Per default the first registered option is used. - * - * @param name The name or key for the object the information should be added to. - * @param infoName The name of the option. The name of the mod as example. - * @param objectInfo The info object that supplies the module with the required information. - */ - public abstract void addInfoToObject(String name, String infoName, I objectInfo); - - /** - * Simple utility function that automatically checks if the object already exists. - * If not, the object is registered. An easy boilerplate code prevention. - * - * @param name - * @param object - */ - public void registerIfNonExistent(String name, T object) { - if (!contains(name)) register(name, object); - } +public abstract class Module { /** * Used to be able to request the name the module is registered as. - * @return The String the module is registered as. So that HMCore.modules.get(name).getName() == name + * @return The String the module is registered as. So that HMCore.modules.get(name).getName() equals name */ public abstract String getName(); - /** - * @return An Array of all objects currently registered to the module. - */ - public abstract T[] getObjects(); + ModuleState moduleState = ModuleState.QUEUED; - // - // For registering the Objects to Hytale - // Made for internal use ONLY - // + public ModuleState getModuleState() { + return moduleState; + } + + protected void setModuleState(ModuleState moduleState) { + this.moduleState = moduleState; + } /** - * Gets called when the phase of object registration to Hytale has come. - * - * @return true when every object has been registered successfully. + * Runs some preparation optionally supplied by the module before it can fully work. + * This should only include stuff that isn't needed for calling functions like register(name, object); to a RegistryModule. This should be in the constructor. + * It can be used as example for creating a namespace or hooking into some Hytale functionalities. + * If you want to run stuff when the Module is loaded, use the constructor of your main class. + * @return true when initialisation finished without a problem. false when there is a critical problem which won't allow the module to function properly. + * When false is returned the server is safely shut down. */ - public abstract boolean registerObjects(); + protected abstract boolean initialize(); + /** + * Gets called when it is time for the module to hook into Hytale and register their stuff in the hytale apis. + * @return true when hooking finished without a problem. false when there is a critical problem which won't allow the module to function properly. + * When false is returned the server is safely shut down. + */ + protected abstract boolean hook(); + + /** + * Gets called when the module should shut down. Can be used to save data a last time. + * @return true when disabling finished without a problem. false when there is a critical problem which won't allow the module to disable properly. + * When false is returned the server is safely shut down with a warning. + */ + protected abstract boolean disable(); + + /** + * Gets called right before the module is unloaded. This will be the last call to the module before it is unloaded. + * @return true when disabling finished without a problem. false when there is a critical problem which won't allow the module to disable properly. + * When false is returned the server is safely shut down with a warning. + */ + protected abstract boolean unload(); - public abstract ObjectInfoData[] getObjectInfoArray(); } diff --git a/src/main/java/org/hmcore/modules/ModuleManager.java b/src/main/java/org/hmcore/modules/ModuleManager.java new file mode 100644 index 0000000..e0ae98a --- /dev/null +++ b/src/main/java/org/hmcore/modules/ModuleManager.java @@ -0,0 +1,101 @@ +package org.hmcore.modules; + +import org.hmcore.HMCore; +import org.hmcore.api.ModuleState; +import org.hmcore.api.exceptions.ModuleErroredException; + +public class ModuleManager { + + static ModuleState overallState = ModuleState.QUEUED; + + public static void loadModules() { + if(overallState != ModuleState.QUEUED) return; + + //TODO: LOAD MODULES HERE LATER + + overallState = ModuleState.LOADED; + + } + + public static void initModules() { + if(overallState != ModuleState.LOADED) return; + + try { + for (Module module: + HMCore.modules.values()) { + if(!module.initialize()) throwStack(module.getName()); + module.setModuleState(ModuleState.INITIALIZED); + } + } catch (ModuleErroredException e) { + e.printStackTrace(); + //TODO: Save worlds etc before exiting? + System.exit(1); + } + + overallState = ModuleState.INITIALIZED; + + } + + public static void hookModules() { + if(overallState != ModuleState.INITIALIZED) return; + + try { + for (Module module: + HMCore.modules.values()) { + if(!module.hook()) throwStack(module.getName()); + module.setModuleState(ModuleState.HOOKED); + } + } catch (ModuleErroredException e) { + e.printStackTrace(); + //TODO: Save worlds etc before exiting? + System.exit(1); + } + + overallState = ModuleState.HOOKED; + + } + + public static void disableModules() { + if(overallState != ModuleState.HOOKED) return; + + try { + for (Module module: + HMCore.modules.values()) { + if(!module.disable()) throwStack(module.getName()); + module.setModuleState(ModuleState.DISABLED); + } + } catch (ModuleErroredException e) { + e.printStackTrace(); + //TODO: Save worlds etc before exiting? + System.exit(1); + } + + overallState = ModuleState.DISABLED; + + } + + public static void unloadModules() { + if(overallState != ModuleState.DISABLED) return; + + try { + for (Module module: + HMCore.modules.values()) { + if(!module.unload()) throwStack(module.getName()); + module.setModuleState(ModuleState.UNLOADED); + } + } catch (ModuleErroredException e) { + e.printStackTrace(); + //TODO: Save worlds etc before exiting? + System.exit(1); + } + + overallState = ModuleState.UNLOADED; + + } + + private static void throwStack(String name) throws ModuleErroredException { + String desc = "Module " + name + " has been found in a errored state while " + overallState.toString(); + overallState = ModuleState.ERRORED; + throw new ModuleErroredException(desc); + } +} diff --git a/src/main/java/org/hmcore/modules/RegistryModule.java b/src/main/java/org/hmcore/modules/RegistryModule.java new file mode 100644 index 0000000..7994719 --- /dev/null +++ b/src/main/java/org/hmcore/modules/RegistryModule.java @@ -0,0 +1,107 @@ +package org.hmcore.modules; + +import org.hmcore.registration.ObjectInfo; +import org.hmcore.registration.config.ObjectInfoData; + +/** + * Represents a module that manages the objects that should get registered to Hytale. + * + * @param The object that should get registered + * @param Object that contains information about the object. Like the texture. + */ +public abstract class RegistryModule extends Module { + + // + // For registering Objects to Module and getting them + // Made for mods to call them + // + + /** + * For checking if the Module has the object already in its list. + * + * @param name The name or registration key of the object to be checked + * @return true when the object is already registered. + */ + public abstract boolean contains(String name); + + /** + * Gets the object for the registered name. + * + * @param name The name or key for the object that should be returned. + * @return The object under the registered name or null if it doesn't exists. + */ + public abstract T get(String name); + + /** + * Registers the object to the module. + * + * @param name The name or key for the object to get registered under. + * @param object The object that should get registered. + */ + public abstract void register(String name, T object); + + // TODO: If one option added stuff the other option doesn't had, (as example ore generation info for a block) it should be automatically added. + // TODO: The admins should be able to choose a option for each feature individually. (Block behavior, ore generation, maybe force a specific texture for users, etc.) + + /** + * Adds an option for information to the object. + * There can be multiple options for information and the server administrators can choose which to use. + * Per default the first registered option is used. + * + * @param name The name or key for the object the information should be added to. + * @param infoName The name of the option. The name of the mod as example. + * @param objectInfo The info object that supplies the module with the required information. + */ + public abstract void addInfoToObject(String name, String infoName, I objectInfo); + + /** + * Simple utility function that automatically checks if the object already exists. + * If not, the object is registered. An easy boilerplate code prevention. + * + * @param name + * @param object + */ + public void registerIfNonExistent(String name, T object) { + if (!contains(name)) register(name, object); + } + + /** + * @return An Array of all objects currently registered to the module. + */ + public abstract T[] getObjects(); + + // + // For registering the Objects to Hytale + // Made for internal use ONLY + // + + /** + * Gets called when the phase of object registration to Hytale has come. + * + * @return true when every object has been registered successfully. + */ + protected abstract boolean registerObjects(); + + public abstract ObjectInfoData[] getObjectInfoArray(); + + /** + * Forces a specific object info to a object so the config the user made is respected. + * @param object The name of the object to force to + * @param objectInfo The object info of the object to apply + */ + public abstract void forceObjectInfoForObject(String object, String objectInfo); + + /** + * Forces a specific object info to a object so the config the user made is respected. + * @param object The name of the object to force to + * @param objectInfo The object info of the object to apply + * @return Returns if both the object and the object info exist. + */ + public abstract boolean objectAndInfoExist(String object, String objectInfo); + + @Override + protected boolean hook() { + registerObjects(); + return true; + } +} diff --git a/src/main/java/org/hmcore/registration/config/ObjectInfoConfigHandler.java b/src/main/java/org/hmcore/registration/config/ObjectInfoConfigHandler.java index 90d0e58..0902bf2 100644 --- a/src/main/java/org/hmcore/registration/config/ObjectInfoConfigHandler.java +++ b/src/main/java/org/hmcore/registration/config/ObjectInfoConfigHandler.java @@ -2,21 +2,106 @@ package org.hmcore.registration.config; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import org.hmcore.HMCore; import org.hmcore.modules.Module; +import org.hmcore.modules.RegistryModule; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; public class ObjectInfoConfigHandler { - private void writeNewConfig() { + public boolean initialize() throws FileNotFoundException { + + new File("config").mkdir(); + + File objectInfoConfigFile = new File("config/object-infos.json"); + if(objectInfoConfigFile.exists()) { + + StringBuilder builder = new StringBuilder(); + + Scanner myReader = new Scanner(objectInfoConfigFile); + while (myReader.hasNextLine()) { + builder.append(myReader.nextLine()); + } + myReader.close(); + + String content = builder.toString(); + ObjectInfoConfig objectInfoConfig = isValid(content); + + if(objectInfoConfig != null) { + + for (ModuleInfo moduleInfo: + objectInfoConfig.modules) { + Module module = HMCore.modules.get(moduleInfo.moduleName); + if(module instanceof RegistryModule) { + RegistryModule registryModule = (RegistryModule) module; + for (ObjectInfoData data: + moduleInfo.objects) { + if(registryModule.objectAndInfoExist(data.objectName, data.objectInfoChoosen)) { + registryModule.forceObjectInfoForObject(data.objectName, data.objectInfoChoosen); + } else { + System.out.println("[!] Either Object Info " + data.objectInfoChoosen + " doesn't exist for " + data.objectName + " or " + data.objectName + " doesn't exist!\n" + + "Please stop the Server, delete objetc-infos.json and let the server regenertate a new config [!]"); + } + } + } + } + + } else { + writeNewConfig(objectInfoConfigFile); + } + } else { + writeNewConfig(objectInfoConfigFile); + } + + return true; } - public static String generateFreshJSON(Module[] modules) { + private void writeNewConfig(File objectInfoConfig) { + try { + FileWriter fileWriter = new FileWriter(objectInfoConfig,false); + List> registryModules = new ArrayList<>(); + Module[] modules = HMCore.modules.values().toArray(new Module[0]); + for (Module module: + modules) { + if(module instanceof RegistryModule) registryModules.add((RegistryModule) module); + } + fileWriter.write(generateFreshJSON(registryModules.toArray(new RegistryModule[0]))); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } - ModuleInfo[] moduleInfos = new ModuleInfo[modules.length]; - for (int i = 0; i < modules.length; i++) { - Module module = modules[i]; - moduleInfos[i] = new ModuleInfo(module.getName(), - module.getObjectInfoArray()); + private static ObjectInfoConfig isValid(String content) { + + ObjectInfoConfig object; + + try { + object = new GsonBuilder().create().fromJson(content, ObjectInfoConfig.class); + } catch (Exception e) { + System.out.println("[!] Object Info Config not valid!"); + return null; + } + + return object; + + } + + public static String generateFreshJSON(RegistryModule[] registryModules) { + + ModuleInfo[] moduleInfos = new ModuleInfo[registryModules.length]; + for (int i = 0; i < registryModules.length; i++) { + RegistryModule registryModule = registryModules[i]; + moduleInfos[i] = new ModuleInfo(registryModule.getName(), + registryModule.getObjectInfoArray()); } ObjectInfoConfig objectInfoConfig = new ObjectInfoConfig(moduleInfos); diff --git a/src/test/java/org/hmcore/tests/JavaTests.java b/src/test/java/org/hmcore/tests/JavaTests.java index 08e5ffb..94504cc 100644 --- a/src/test/java/org/hmcore/tests/JavaTests.java +++ b/src/test/java/org/hmcore/tests/JavaTests.java @@ -1,12 +1,14 @@ package org.hmcore.tests; import org.hmcore.HMCore; -import org.hmcore.modules.Module; +import org.hmcore.modules.RegistryModule; import org.hmcore.registration.config.ObjectInfoConfigHandler; import org.hmcore.tests.modules.impl.JavaCustomObjectInfo; -import org.hmcore.tests.modules.impl.JavaTestModule; +import org.hmcore.tests.modules.impl.JavaTestRegistryModule; import org.junit.jupiter.api.Test; +import java.io.FileNotFoundException; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,9 +17,9 @@ public class JavaTests { @Test public void testModuleFunctionality() { - HMCore.modules.putIfAbsent("java_test", new JavaTestModule()); + HMCore.modules.putIfAbsent("java_test", new JavaTestRegistryModule()); - JavaTestModule testModule = (JavaTestModule) HMCore.modules.get("java_test"); + JavaTestRegistryModule testModule = (JavaTestRegistryModule) HMCore.modules.get("java_test"); // // Default registration tests @@ -52,7 +54,40 @@ public class JavaTests { testModule.addInfoToObject("test3", "opt5", new JavaCustomObjectInfo("dada", 32833)); testModule.addInfoToObject("test3", "opt6", new JavaCustomObjectInfo("e3312", 2130440)); - System.out.println(ObjectInfoConfigHandler.generateFreshJSON(HMCore.modules.values().toArray(new Module[0]))); + assertEquals("{\n" + + " \"modules\": [\n" + + " {\n" + + " \"moduleName\": \"java_test\",\n" + + " \"objects\": [\n" + + " {\n" + + " \"objectName\": \"test2\",\n" + + " \"objectInfoChoosen\": \"default\",\n" + + " \"_availableOptions\": \"opt3, opt2\"\n" + + " },\n" + + " {\n" + + " \"objectName\": \"test3\",\n" + + " \"objectInfoChoosen\": \"default\",\n" + + " \"_availableOptions\": \"opt4, opt5, opt6\"\n" + + " },\n" + + " {\n" + + " \"objectName\": \"test1\",\n" + + " \"objectInfoChoosen\": \"default\",\n" + + " \"_availableOptions\": \"opt1\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}", ObjectInfoConfigHandler.generateFreshJSON(HMCore.modules.values().toArray(new RegistryModule[0])), "JSON String generation working"); + + } + + @Test + public void testConfigCreation() throws FileNotFoundException { + + ObjectInfoConfigHandler objectInfoConfigHandler = new ObjectInfoConfigHandler(); + + objectInfoConfigHandler.initialize(); + } diff --git a/src/test/java/org/hmcore/tests/modules/impl/JavaTestModule.java b/src/test/java/org/hmcore/tests/modules/impl/JavaTestRegistryModule.java similarity index 76% rename from src/test/java/org/hmcore/tests/modules/impl/JavaTestModule.java rename to src/test/java/org/hmcore/tests/modules/impl/JavaTestRegistryModule.java index 7a313ff..c701e28 100644 --- a/src/test/java/org/hmcore/tests/modules/impl/JavaTestModule.java +++ b/src/test/java/org/hmcore/tests/modules/impl/JavaTestRegistryModule.java @@ -1,6 +1,6 @@ package org.hmcore.tests.modules.impl; -import org.hmcore.modules.Module; +import org.hmcore.modules.RegistryModule; import org.hmcore.registration.config.ObjectInfoData; import java.util.ArrayList; @@ -8,10 +8,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class JavaTestModule extends Module { +public class JavaTestRegistryModule extends RegistryModule { public HashMap objectMap = new HashMap<>(); public HashMap> infoMap = new HashMap<>(); + public HashMap forceMap = new HashMap<>(); public List endList = new ArrayList<>(); @@ -43,6 +44,21 @@ public class JavaTestModule extends Module { return "java_test"; } + @Override + protected boolean initialize() { + return true; + } + + @Override + protected boolean disable() { + return true; + } + + @Override + protected boolean unload() { + return true; + } + @Override public Integer[] getObjects() { return objectMap.values().toArray(new Integer[0]); @@ -99,4 +115,14 @@ public class JavaTestModule extends Module { return objectInfoData; } + + @Override + public void forceObjectInfoForObject(String object, String objectInfo) { + forceMap.put(objectMap.get(object), infoMap.get(object).get(objectInfo)); + } + + @Override + public boolean objectAndInfoExist(String object, String objectInfo) { + return infoMap.containsKey(object) && infoMap.get(object).containsKey(objectInfo); + } }