diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModule.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModule.java index 186c77fa..1f785e81 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModule.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModule.java @@ -10,12 +10,16 @@ public abstract class LauncherModule { protected LauncherModulesManager modulesManager; protected final LauncherModuleInfo moduleInfo; protected ModulesConfigManager modulesConfigManager; - InitPhase initPhase = InitPhase.CREATED; + protected InitPhase initPhase = InitPhase.CREATED; protected LauncherModule() { moduleInfo = new LauncherModuleInfo("UnknownModule"); } + public LauncherModuleInfo getModuleInfo() { + return moduleInfo; + } + public enum InitPhase { CREATED, @@ -38,32 +42,41 @@ public Event cancel() { return this; } - protected boolean cancel; + protected boolean cancel = false; } public InitPhase getInitPhase() { return initPhase; } - Map, EventHandler> setContext(LauncherModulesContext context) + public LauncherModule setInitPhase(InitPhase initPhase) { + this.initPhase = initPhase; + return this; + } + + public void setContext(LauncherModulesContext context) { if(this.context != null) throw new IllegalStateException("Module already set context"); this.context = context; this.modulesManager = context.getModulesManager(); this.modulesConfigManager = context.getModulesConfigManager(); - return eventMap; + } + + public void preInit() { + //NOP } public abstract void init(); - boolean registerEvent(EventHandler handle, Class tClass) + + protected boolean registerEvent(EventHandler handle, Class tClass) { eventMap.put(tClass, handle); return true; } @SuppressWarnings("unchecked") - void callEvent(T event) throws Exception + public final void callEvent(T event) throws Exception { Class tClass = event.getClass(); for(Map.Entry, EventHandler> e : eventMap.entrySet()) diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModulesManager.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModulesManager.java index 788c88c3..d324203f 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModulesManager.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/LauncherModulesManager.java @@ -2,11 +2,15 @@ import pro.gravit.utils.Version; +import java.io.IOException; +import java.nio.file.Path; import java.util.function.Predicate; public interface LauncherModulesManager { LauncherModule loadModule(LauncherModule module); + LauncherModule loadModule(Path file) throws IOException; LauncherModule getModule(String name); + LauncherModule getCoreModule(); default boolean containsModule(String name) { return getModule(name) != null; diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitPhase.java similarity index 63% rename from LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitEvent.java rename to LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitPhase.java index ec67f096..b4f24adb 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/InitPhase.java @@ -2,5 +2,5 @@ import pro.gravit.launcher.modules.LauncherModule; -public interface InitEvent extends LauncherModule.Event { +public class InitPhase extends LauncherModule.Event { } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitEvent.java deleted file mode 100644 index f92ab1d0..00000000 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package pro.gravit.launcher.modules.events; - -public interface PostInitEvent { -} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreInitEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitPhase.java similarity index 61% rename from LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreInitEvent.java rename to LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitPhase.java index 2c2b2cbf..88d097a5 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreInitEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PostInitPhase.java @@ -2,5 +2,5 @@ import pro.gravit.launcher.modules.LauncherModule; -public interface PreInitEvent extends LauncherModule.Event { +public class PostInitPhase extends LauncherModule.Event { } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/WrapperEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreConfigPhase.java similarity index 61% rename from LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/WrapperEvent.java rename to LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreConfigPhase.java index 919e7a88..01630a53 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/WrapperEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreConfigPhase.java @@ -2,5 +2,5 @@ import pro.gravit.launcher.modules.LauncherModule; -public interface WrapperEvent extends LauncherModule.Event { +public class PreConfigPhase extends LauncherModule.Event { } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreGsonPhase.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreGsonPhase.java new file mode 100644 index 00000000..73a5fe17 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/PreGsonPhase.java @@ -0,0 +1,14 @@ +package pro.gravit.launcher.modules.events; + +import com.google.gson.GsonBuilder; +import pro.gravit.launcher.modules.LauncherModule; + +public class PreGsonPhase extends LauncherModule.Event { + public GsonBuilder gsonBuilder; + public GsonBuilder gsonConfigBuilder; + + public PreGsonPhase(GsonBuilder gsonBuilder, GsonBuilder gsonConfigBuilder) { + this.gsonBuilder = gsonBuilder; + this.gsonConfigBuilder = gsonConfigBuilder; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleContext.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleContext.java new file mode 100644 index 00000000..c7f87e45 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleContext.java @@ -0,0 +1,24 @@ +package pro.gravit.launcher.modules.impl; + +import pro.gravit.launcher.modules.LauncherModulesContext; +import pro.gravit.launcher.modules.LauncherModulesManager; +import pro.gravit.launcher.modules.ModulesConfigManager; + +public class SimpleModuleContext implements LauncherModulesContext { + public final LauncherModulesManager modulesManager; + public final ModulesConfigManager configManager; + @Override + public LauncherModulesManager getModulesManager() { + return modulesManager; + } + + @Override + public ModulesConfigManager getModulesConfigManager() { + return configManager; + } + + public SimpleModuleContext(LauncherModulesManager modulesManager, ModulesConfigManager configManager) { + this.modulesManager = modulesManager; + this.configManager = configManager; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java new file mode 100644 index 00000000..a189d2c2 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/impl/SimpleModuleManager.java @@ -0,0 +1,189 @@ +package pro.gravit.launcher.modules.impl; + +import pro.gravit.launcher.managers.SimpleModulesConfigManager; +import pro.gravit.launcher.modules.LauncherModule; +import pro.gravit.launcher.modules.LauncherModuleInfo; +import pro.gravit.launcher.modules.LauncherModulesManager; +import pro.gravit.launcher.modules.ModulesConfigManager; +import pro.gravit.utils.PublicURLClassLoader; +import pro.gravit.utils.Version; +import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.LogHelper; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.jar.JarFile; + +public class SimpleModuleManager implements LauncherModulesManager { + protected final List modules = new ArrayList<>(); + protected final List moduleNames = new ArrayList<>(); + protected final SimpleModuleContext context; + protected final ModulesConfigManager modulesConfigManager; + + protected PublicURLClassLoader classLoader = new PublicURLClassLoader(new URL[]{}); + + protected final class ModulesVisitor extends SimpleFileVisitor { + private ModulesVisitor() { + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + loadModule(file); + return super.visitFile(file, attrs); + } + } + + public void autoload(Path dir) throws IOException { + if (Files.notExists(dir)) Files.createDirectory(dir); + else { + IOHelper.walk(dir, new ModulesVisitor(), true); + } + } + + public void initModules() { + List startedModules = Collections.unmodifiableList(new ArrayList<>(modules)); + for(LauncherModule module : startedModules) + { + module.preInit(); + } + boolean isAnyModuleLoad = true; + int modules_size = modules.size(); + int loaded = 0; + while(isAnyModuleLoad) + { + isAnyModuleLoad = false; + for(LauncherModule module : modules) + { + if(!module.getInitPhase().equals(LauncherModule.InitPhase.CREATED)) continue; + if(checkDepend(module)) + { + isAnyModuleLoad = true; + module.setInitPhase(LauncherModule.InitPhase.INIT); + module.init(); + module.setInitPhase(LauncherModule.InitPhase.FINISH); + loaded++; + } + } + } + if(modules_size == loaded) return; + if(loaded > modules_size) throw new IllegalStateException(String.format("Module loading error. %d > %d", loaded, modules_size)); //WTF? + for(LauncherModule module : modules) + { + if(module.getInitPhase().equals(LauncherModule.InitPhase.CREATED)) + { + LauncherModuleInfo info = module.getModuleInfo(); + LogHelper.warning("Module %s required %s. Cyclic dependencies?", info.name, Arrays.toString(info.dependencies)); + module.setInitPhase(LauncherModule.InitPhase.INIT); + module.init(); + module.setInitPhase(LauncherModule.InitPhase.FINISH); + } + } + } + + private boolean checkDepend(LauncherModule module) + { + LauncherModuleInfo info = module.getModuleInfo(); + for(String dep : info.dependencies) + { + LauncherModule depModule = getModule(dep); + if(depModule == null) throw new RuntimeException(String.format("Module %s required %s. %s not found", info.name, dep, dep)); + if(depModule.getInitPhase().equals(LauncherModule.InitPhase.CREATED)) return false; + } + return true; + } + + public SimpleModuleManager(Path modulesDir, Path configDir) { + modulesConfigManager = new SimpleModulesConfigManager(configDir); + context = new SimpleModuleContext(this, modulesConfigManager); + } + + @Override + public LauncherModule loadModule(LauncherModule module) { + modules.add(module); + LauncherModuleInfo info = module.getModuleInfo(); + moduleNames.add(info.name); + module.setContext(context); + return module; + } + + @Override + public LauncherModule loadModule(Path file) throws IOException { + try (JarFile f = new JarFile(file.toFile())) { + String moduleClass = f.getManifest().getMainAttributes().getValue("Module-Main-Class"); + if(moduleClass == null) + { + LogHelper.error("In module %s Module-Main-Class not found", file.toString()); + return null; + } + classLoader.addURL(file.toUri().toURL()); + LauncherModule module = (LauncherModule) Class.forName(moduleClass, true, classLoader).newInstance(); + loadModule(module); + return module; + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + LogHelper.error(e); + LogHelper.error("In module %s Module-Main-Class incorrect", file.toString()); + return null; + } + } + + @Override + public LauncherModule getModule(String name) { + for(LauncherModule module : modules) + { + LauncherModuleInfo info = module.getModuleInfo(); + if(info.name.equals(name)) return module; + } + return null; + } + + @Override + public LauncherModule getCoreModule() { + return null; + } + + @Override + public ClassLoader getModuleClassLoader() { + return classLoader; + } + + @Override + @SuppressWarnings("unchecked") + public T getModule(Class clazz) { + for(LauncherModule module : modules) + { + if(clazz.isAssignableFrom(module.getClass())) return (T) module; + } + return null; + } + + @Override + @SuppressWarnings("unchecked") + public T findModule(Class clazz, Predicate versionPredicate) { + for(LauncherModule module : modules) + { + LauncherModuleInfo info = module.getModuleInfo(); + if(!versionPredicate.test(info.version)) continue; + if(clazz.isAssignableFrom(module.getClass())) return (T) module; + } + return null; + } + + @Override + public void invokeEvent(T event) throws Exception { + for(LauncherModule module : modules) + { + module.callEvent(event); + if(event.isCancel()) return; + } + } +}