diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 4ef2fc9d09..9d0a51ebab 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -130,6 +130,7 @@ public class Vars implements Loadable{ /** list of all locales that can be switched to */ public static Locale[] locales; + public static FileTree filet; public static Net net; public static ContentLoader content; public static GameState state; @@ -193,6 +194,7 @@ public class Vars implements Loadable{ Version.init(); + filet = new FileTree(); mods = new Mods(); content = new ContentLoader(); loops = new LoopControl(); diff --git a/core/src/io/anuke/mindustry/core/FileTree.java b/core/src/io/anuke/mindustry/core/FileTree.java new file mode 100644 index 0000000000..19b2658061 --- /dev/null +++ b/core/src/io/anuke/mindustry/core/FileTree.java @@ -0,0 +1,59 @@ +package io.anuke.mindustry.core; + +import io.anuke.arc.*; +import io.anuke.arc.collection.*; +import io.anuke.arc.files.*; +import io.anuke.arc.util.*; +import io.anuke.arc.util.io.*; +import io.anuke.mindustry.mod.Mods.*; + +/** Handles files in a modded context. */ +public class FileTree{ + private ObjectMap files = new ObjectMap<>(); + private ObjectMap> bundles = new ObjectMap<>(); + + public void buildFiles(Array mods){ + //TODO many files should not be replaced + for(LoadedMod mod : mods){ + mod.root.walk(f -> { + //TODO calling child/parent on these files will give you gibberish; create wrapper class. + files.put(f.path(), f); + }); + + //load up bundles. + FileHandle folder = mod.root.child("bundles"); + if(folder.exists()){ + for(FileHandle file : folder.list()){ + if(file.name().startsWith("bundle") && file.extension().equals("properties")){ + String name = file.nameWithoutExtension(); + bundles.getOr(name, Array::new).add(file); + } + } + } + } + + //add new keys to each bundle + I18NBundle bundle = Core.bundle; + while(bundle != null){ + String str = bundle.getLocale().toString(); + String locale = "bundle" + (str.isEmpty() ? "" : "_" + str); + for(FileHandle file : bundles.getOr(locale, Array::new)){ + try{ + PropertiesUtils.load(bundle.getProperties(), file.reader()); + }catch(Exception e){ + throw new RuntimeException("Error loading bundle: " + file + "/" + locale, e); + } + } + bundle = bundle.getParent(); + } + } + + /** Gets an asset file.*/ + public FileHandle get(String path){ + if(files.containsKey(path)){ + return files.get(path); + }else{ + return Core.files.internal(path); + } + } +} diff --git a/core/src/io/anuke/mindustry/mod/Mods.java b/core/src/io/anuke/mindustry/mod/Mods.java index 9499bae006..fcbebd7921 100644 --- a/core/src/io/anuke/mindustry/mod/Mods.java +++ b/core/src/io/anuke/mindustry/mod/Mods.java @@ -23,15 +23,15 @@ public class Mods{ return modDirectory.child(load.name).child("config.json"); } - /** @return the loaded plugin found by class, or null if not found. */ + /** @return the loaded mod found by class, or null if not found. */ public @Nullable LoadedMod getMod(Class type){ return loaded.find(l -> l.mod.getClass() == type); } - /** Loads all plugins from the folder, but does call any methods on them.*/ + /** Loads all mods from the folder, but does call any methods on them.*/ public void load(){ for(FileHandle file : modDirectory.list()){ - if(!file.extension().equals("jar") || !file.extension().equals("zi[")) continue; + if(!file.extension().equals("jar") || !file.extension().equals("zip")) continue; try{ loaded.add(loadmod(file)); @@ -41,16 +41,18 @@ public class Mods{ e.printStackTrace(); } } + + filet.buildFiles(loaded); } - /** @return all loaded plugins. */ + /** @return all loaded mods. */ public Array all(){ return loaded; } - /** Iterates through each plugin.*/ + /** Iterates through each mod with a main class.*/ public void each(Consumer cons){ - loaded.each(p -> cons.accept(p.mod)); + loaded.each(p -> p.mod != null, p -> cons.accept(p.mod)); } private LoadedMod loadmod(FileHandle jar) throws Exception{ @@ -63,23 +65,37 @@ public class Mods{ } ModMeta meta = JsonIO.read(ModMeta.class, metaf.readString()); + String camelized = meta.name.replace(" ", ""); + String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main; + Mod mainMod; - URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader()); - Class main = classLoader.loadClass(meta.main); - metas.put(main, meta); - return new LoadedMod(jar, zip, (Mod)main.getDeclaredConstructor().newInstance(), meta); + //make sure the main class exists before loading it; if it doesn't just don't put it there + if(zip.child(mainClass.replace('.', '/') + ".class").exists()){ + URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader()); + Class main = classLoader.loadClass(mainClass); + metas.put(main, meta); + mainMod = (Mod)main.getDeclaredConstructor().newInstance(); + }else{ + mainMod = null; + } + + return new LoadedMod(jar, zip, mainMod, meta); } /** Represents a plugin that has been loaded from a jar file.*/ public static class LoadedMod{ - public final FileHandle jarFile; - public final FileHandle zipRoot; + /** The location of this mod's zip file on the disk. */ + public final FileHandle file; + /** The root zip file; points to the contents of this mod. */ + public final FileHandle root; + /** The mod's main class; may be null. */ public final @Nullable Mod mod; + /** This mod's metadata. */ public final ModMeta meta; - public LoadedMod(FileHandle jarFile, FileHandle zipRoot, Mod mod, ModMeta meta){ - this.zipRoot = zipRoot; - this.jarFile = jarFile; + public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){ + this.root = root; + this.file = file; this.mod = mod; this.meta = meta; } @@ -88,5 +104,6 @@ public class Mods{ /** Plugin metadata information.*/ public static class ModMeta{ public String name, author, description, version, main; + public String[] dependencies = {}; //TODO implement } } diff --git a/gradle.properties b/gradle.properties index f5a430ccc2..5238d2620d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=5bf89742e927d1951c58cc2743814874c1c9ad25 +archash=9f30453676bf2b582c01a46a60965305321dcb9d diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 38eaf7bc72..974f9e129e 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -332,7 +332,7 @@ public class ServerControl implements ApplicationListener{ info("Name: &ly{0}", mod.meta.name); info("Version: &ly{0}", mod.meta.version); info("Author: &ly{0}", mod.meta.author); - info("Path: &ly{0}", mod.jarFile.path()); + info("Path: &ly{0}", mod.file.path()); info("Description: &ly{0}", mod.meta.description); }else{ info("No mod with name &ly'{0}'&lg found.");