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 (^///^)
This commit is contained in:
UnrealValentin
2021-04-29 18:10:37 +02:00
parent f7105ad3e6
commit 3d804a0de1
10 changed files with 450 additions and 97 deletions

9
.gitignore vendored
View File

@@ -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
@@ -209,3 +209,4 @@ gradle-app.setting
# End of https://www.toptal.com/developers/gitignore/api/java,eclipse,intellij,gradle
run/
test-run/

View File

@@ -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<String, Module<?, ?>> modules = new HashMap<>();
public static final HashMap<String, Module> 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();
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,7 @@
package org.hmcore.api.exceptions;
public class ModuleErroredException extends Exception{
public ModuleErroredException(String s) {
super(s);
}
}

View File

@@ -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 <T> The object that should get registered
* @param <I> Object that contains information about the object. Like the texture.
* Represents a module with custom features
*/
public abstract class Module<T, I extends ObjectInfo> {
//
// 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();
}

View File

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

View File

@@ -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 <T> The object that should get registered
* @param <I> Object that contains information about the object. Like the texture.
*/
public abstract class RegistryModule<T, I extends ObjectInfo> 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;
}
}

View File

@@ -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<RegistryModule<?, ?>> 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);

View File

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

View File

@@ -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<Integer, JavaCustomObjectInfo> {
public class JavaTestRegistryModule extends RegistryModule<Integer, JavaCustomObjectInfo> {
public HashMap<String, Integer> objectMap = new HashMap<>();
public HashMap<String, HashMap<String, JavaCustomObjectInfo>> infoMap = new HashMap<>();
public HashMap<Integer, JavaCustomObjectInfo> forceMap = new HashMap<>();
public List<Object> endList = new ArrayList<>();
@@ -43,6 +44,21 @@ public class JavaTestModule extends Module<Integer, JavaCustomObjectInfo> {
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<Integer, JavaCustomObjectInfo> {
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);
}
}