API cleanup

This commit is contained in:
Anuken
2019-12-14 18:19:02 -05:00
parent 9d3dda035c
commit e043f4bb66
6 changed files with 63 additions and 147 deletions

View File

@@ -219,7 +219,7 @@ public class NetServer implements ApplicationListener{
@Override @Override
public void init(){ public void init(){
mods.each(mod -> mod.registerClientCommands(clientCommands)); mods.eachClass(mod -> mod.registerClientCommands(clientCommands));
} }
private void registerCommands(){ private void registerCommands(){

View File

@@ -33,13 +33,12 @@ public class Mods implements Loadable{
private @Nullable Scripts scripts; private @Nullable Scripts scripts;
private ContentParser parser = new ContentParser(); private ContentParser parser = new ContentParser();
private ObjectMap<String, Array<FileHandle>> bundles = new ObjectMap<>(); private ObjectMap<String, Array<FileHandle>> bundles = new ObjectMap<>();
private ObjectSet<String> specialFolders = ObjectSet.with("bundles", "sprites"); private ObjectSet<String> specialFolders = ObjectSet.with("bundles", "sprites", "sprites-override");
private int totalSprites; private int totalSprites;
private MultiPacker packer; private MultiPacker packer;
private Array<LoadedMod> loaded = new Array<>(); private Array<LoadedMod> mods = new Array<>();
private Array<LoadedMod> disabled = new Array<>();
private ObjectMap<Class<?>, ModMeta> metas = new ObjectMap<>(); private ObjectMap<Class<?>, ModMeta> metas = new ObjectMap<>();
private boolean requiresReload; private boolean requiresReload;
@@ -53,19 +52,19 @@ public class Mods implements Loadable{
/** Returns a list of files per mod subdirectory. */ /** Returns a list of files per mod subdirectory. */
public void listFiles(String directory, Cons2<LoadedMod, FileHandle> cons){ public void listFiles(String directory, Cons2<LoadedMod, FileHandle> cons){
for(LoadedMod mod : loaded){ eachEnabled(mod -> {
FileHandle file = mod.root.child(directory); FileHandle file = mod.root.child(directory);
if(file.exists()){ if(file.exists()){
for(FileHandle child : file.list()){ for(FileHandle child : file.list()){
cons.get(mod, child); cons.get(mod, child);
} }
} }
} });
} }
/** @return the loaded mod 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<? extends Mod> type){ public @Nullable LoadedMod getMod(Class<? extends Mod> type){
return loaded.find(l -> l.mod != null && l.mod.getClass() == type); return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);//loaded.find(l -> l.mod != null && l.mod.getClass() == type);
} }
/** Imports an external mod file.*/ /** Imports an external mod file.*/
@@ -77,7 +76,7 @@ public class Mods implements Loadable{
file.copyTo(dest); file.copyTo(dest);
try{ try{
loaded.add(loadMod(dest)); mods.add(loadMod(dest));
requiresReload = true; requiresReload = true;
}catch(IOException e){ }catch(IOException e){
dest.delete(); dest.delete();
@@ -91,19 +90,19 @@ public class Mods implements Loadable{
/** Repacks all in-game sprites. */ /** Repacks all in-game sprites. */
@Override @Override
public void loadAsync(){ public void loadAsync(){
if(loaded.isEmpty()) return; if(!mods.contains(LoadedMod::enabled)) return;
Time.mark(); Time.mark();
packer = new MultiPacker(); packer = new MultiPacker();
for(LoadedMod mod : loaded){ eachEnabled(mod -> {
Array<FileHandle> sprites = mod.root.child("sprites").findAll(f -> f.extension().equals("png")); Array<FileHandle> sprites = mod.root.child("sprites").findAll(f -> f.extension().equals("png"));
Array<FileHandle> overrides = mod.root.child("sprites-override").findAll(f -> f.extension().equals("png")); Array<FileHandle> overrides = mod.root.child("sprites-override").findAll(f -> f.extension().equals("png"));
packSprites(sprites, mod, true); packSprites(sprites, mod, true);
packSprites(overrides, mod, false); packSprites(overrides, mod, false);
Log.debug("Packed {0} images for mod '{1}'.", sprites.size + overrides.size, mod.meta.name); Log.debug("Packed {0} images for mod '{1}'.", sprites.size + overrides.size, mod.meta.name);
totalSprites += sprites.size + overrides.size; totalSprites += sprites.size + overrides.size;
} });
for(AtlasRegion region : Core.atlas.getRegions()){ for(AtlasRegion region : Core.atlas.getRegions()){
PageType type = getPage(region); PageType type = getPage(region);
@@ -198,8 +197,7 @@ public class Mods implements Loadable{
ui.showErrorMessage("$mod.delete.error"); ui.showErrorMessage("$mod.delete.error");
return; return;
} }
loaded.remove(mod); mods.remove(mod);
disabled.remove(mod);
requiresReload = true; requiresReload = true;
} }
@@ -225,11 +223,7 @@ public class Mods implements Loadable{
Log.debug("[Mods] Loading mod {0}", file); Log.debug("[Mods] Loading mod {0}", file);
try{ try{
LoadedMod mod = loadMod(file); LoadedMod mod = loadMod(file);
if(mod.enabled() || headless){ mods.add(mod);
loaded.add(mod);
}else{
disabled.add(mod);
}
}catch(Exception e){ }catch(Exception e){
Log.err("Failed to load mod file {0}. Skipping.", file); Log.err("Failed to load mod file {0}. Skipping.", file);
Log.err(e); Log.err(e);
@@ -240,11 +234,7 @@ public class Mods implements Loadable{
for(FileHandle file : platform.getWorkshopContent(LoadedMod.class)){ for(FileHandle file : platform.getWorkshopContent(LoadedMod.class)){
try{ try{
LoadedMod mod = loadMod(file); LoadedMod mod = loadMod(file);
if(mod.enabled()){ mods.add(mod);
loaded.add(mod);
}else{
disabled.add(mod);
}
mod.addSteamID(file.name()); mod.addSteamID(file.name());
}catch(Exception e){ }catch(Exception e){
Log.err("Failed to load mod workshop file {0}. Skipping.", file); Log.err("Failed to load mod workshop file {0}. Skipping.", file);
@@ -252,28 +242,24 @@ public class Mods implements Loadable{
} }
} }
resolveDependencies(); resolveModState();
//sort mods to make sure servers handle them properly. //sort mods to make sure servers handle them properly.
loaded.sort(Structs.comparing(m -> m.name)); mods.sort(Structs.comparing(m -> m.name));
buildFiles(); buildFiles();
} }
private void resolveDependencies(){ private void resolveModState(){
Array<LoadedMod> incompatible = loaded.select(m -> !m.isSupported()); mods.each(this::updateDependencies);
loaded.removeAll(incompatible);
disabled.addAll(incompatible);
for(LoadedMod mod : Array.<LoadedMod>withArrays(loaded, disabled)){ for(LoadedMod mod : mods){
updateDependencies(mod); mod.state =
!mod.isSupported() ? ModState.unsupported :
mod.hasUnmetDependencies() ? ModState.missingDependencies :
!mod.enabled() /*TODO check disabled state!*/ ? ModState.disabled :
ModState.enabled;
} }
disabled.addAll(loaded.select(LoadedMod::hasUnmetDependencies));
loaded.removeAll(LoadedMod::hasUnmetDependencies);
disabled.each(mod -> setEnabled(mod, false));
disabled.distinct();
loaded.distinct();
} }
private void updateDependencies(LoadedMod mod){ private void updateDependencies(LoadedMod mod){
@@ -298,16 +284,16 @@ public class Mods implements Loadable{
private Array<LoadedMod> orderedMods(){ private Array<LoadedMod> orderedMods(){
ObjectSet<LoadedMod> visited = new ObjectSet<>(); ObjectSet<LoadedMod> visited = new ObjectSet<>();
Array<LoadedMod> result = new Array<>(); Array<LoadedMod> result = new Array<>();
for(LoadedMod mod : loaded){ eachEnabled(mod -> {
if(!visited.contains(mod)){ if(!visited.contains(mod)){
topoSort(mod, result, visited); topoSort(mod, result, visited);
} }
} });
return result; return result;
} }
private LoadedMod locateMod(String name){ private LoadedMod locateMod(String name){
return loaded.find(mod -> mod.name.equals(name)); return mods.find(mod -> mod.enabled() && mod.name.equals(name));
} }
private void buildFiles(){ private void buildFiles(){
@@ -357,8 +343,7 @@ public class Mods implements Loadable{
//TODO make it less epic //TODO make it less epic
Core.atlas = new TextureAtlas(Core.files.internal("sprites/sprites.atlas")); Core.atlas = new TextureAtlas(Core.files.internal("sprites/sprites.atlas"));
loaded.clear(); mods.clear();
disabled.clear();
load(); load();
Sounds.dispose(); Sounds.dispose();
Sounds.load(); Sounds.load();
@@ -392,7 +377,7 @@ public class Mods implements Loadable{
Time.mark(); Time.mark();
try{ try{
for(LoadedMod mod : loaded){ eachEnabled(mod -> {
if(mod.root.child("scripts").exists()){ if(mod.root.child("scripts").exists()){
content.setCurrentMod(mod); content.setCurrentMod(mod);
mod.scripts = mod.root.child("scripts").findAll(f -> f.extension().equals("js")); mod.scripts = mod.root.child("scripts").findAll(f -> f.extension().equals("js"));
@@ -414,7 +399,7 @@ public class Mods implements Loadable{
} }
} }
} }
} });
}finally{ }finally{
content.setCurrentMod(null); content.setCurrentMod(null);
} }
@@ -486,41 +471,18 @@ public class Mods implements Loadable{
parser.markError(content, error); parser.markError(content, error);
} }
/** @return all loaded mods. */
public Array<LoadedMod> all(){
return loaded;
}
/** @return all disabled mods. */
public Array<LoadedMod> disabled(){
return disabled;
}
/** @return a list of mod names only, without versions. */
public Array<String> getModNames(){
return loaded.select(l -> !l.meta.hidden).map(l -> l.name + ":" + l.meta.version);
}
/** @return a list of mods and versions, in the format name:version. */ /** @return a list of mods and versions, in the format name:version. */
public Array<String> getModStrings(){ public Array<String> getModStrings(){
return loaded.select(l -> !l.meta.hidden).map(l -> l.name + ":" + l.meta.version); return mods.select(l -> !l.meta.hidden && l.enabled()).map(l -> l.name + ":" + l.meta.version);
} }
/** Makes a mod enabled or disabled. shifts it.*/ /** Makes a mod enabled or disabled. shifts it.*/
public void setEnabled(LoadedMod mod, boolean enabled){ public void setEnabled(LoadedMod mod, boolean enabled){
if(mod.enabled() != enabled){ if(mod.enabled() != enabled){
Core.settings.putSave("mod-" + mod.name + "-enabled", enabled); Core.settings.putSave("mod-" + mod.name + "-enabled", enabled);
Core.settings.save();
requiresReload = true; requiresReload = true;
if(!enabled){ mod.state = enabled ? ModState.enabled : ModState.disabled;
loaded.remove(mod); mods.each(this::updateDependencies);
if(!disabled.contains(mod)) disabled.add(mod);
}else{
if(!loaded.contains(mod)) loaded.add(mod);
disabled.remove(mod);
}
loaded.each(this::updateDependencies);
disabled.each(this::updateDependencies);
} }
} }
@@ -537,38 +499,19 @@ public class Mods implements Loadable{
return result; return result;
} }
/** Iterates through each mod with a main class.*/ public Array<LoadedMod> list(){
public void each(Cons<Mod> cons){ return mods;
loaded.each(p -> p.mod != null, p -> contextRun(p, () -> cons.get(p.mod)));
} }
/* /** Iterates through each mod with a main class. */
public void handleError(Throwable t, LoadedMod mod){ public void eachClass(Cons<Mod> cons){
Array<Throwable> causes = Strings.getCauses(t); mods.each(p -> p.main != null, p -> contextRun(p, () -> cons.get(p.main)));
Content content = null; }
for(Throwable e : causes){
if(e instanceof ModLoadException && ((ModLoadException) e).content != null){
content = ((ModLoadException) e).content;
}
}
String realCause = "<???>"; /** Iterates through each enabled mod. */
for(int i = causes.size -1 ; i >= 0; i--){ public void eachEnabled(Cons<LoadedMod> cons){
if(causes.get(i).getMessage() != null){ mods.each(LoadedMod::enabled, cons);
realCause = causes.get(i).getMessage(); }
break;
}
}
setEnabled(mod, false);
if(content != null){
throw new ModLoadException(Strings.format("Error loading '{0}' from mod '{1}' ({2}):\n{3}",
content, mod.meta.name, content.sourceFile == null ? "<unknown file>" : content.sourceFile.name(), realCause), content, t);
}else{
throw new ModLoadException("Error loading mod " + mod.meta.name, t);
}
}*/
public void contextRun(LoadedMod mod, Runnable run){ public void contextRun(LoadedMod mod, Runnable run){
try{ try{
@@ -597,7 +540,7 @@ public class Mods implements Loadable{
String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main; String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main;
String baseName = meta.name.toLowerCase().replace(" ", "-"); String baseName = meta.name.toLowerCase().replace(" ", "-");
if(loaded.contains(m -> m.name.equals(baseName)) || disabled.contains(m -> m.name.equals(baseName))){ if(mods.contains(m -> m.name.equals(baseName))){
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported."); throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
} }
@@ -641,7 +584,7 @@ public class Mods implements Loadable{
/** The root zip file; points to the contents of this mod. In the case of folders, this is the same as the mod's file. */ /** The root zip file; points to the contents of this mod. In the case of folders, this is the same as the mod's file. */
public final FileHandle root; public final FileHandle root;
/** The mod's main class; may be null. */ /** The mod's main class; may be null. */
public final @Nullable Mod mod; public final @Nullable Mod main;
/** Internal mod name. Used for textures. */ /** Internal mod name. Used for textures. */
public final String name; public final String name;
/** This mod's metadata. */ /** This mod's metadata. */
@@ -652,17 +595,19 @@ public class Mods implements Loadable{
public Array<String> missingDependencies = new Array<>(); public Array<String> missingDependencies = new Array<>();
/** Script files to run. */ /** Script files to run. */
public Array<FileHandle> scripts = new Array<>(); public Array<FileHandle> scripts = new Array<>();
/** Current state of this mod. */
public ModState state = ModState.disabled;
public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){ public LoadedMod(FileHandle file, FileHandle root, Mod main, ModMeta meta){
this.root = root; this.root = root;
this.file = file; this.file = file;
this.mod = mod; this.main = main;
this.meta = meta; this.meta = meta;
this.name = meta.name.toLowerCase().replace(" ", "-"); this.name = meta.name.toLowerCase().replace(" ", "-");
} }
public boolean enabled(){ public boolean enabled(){
return Core.settings.getBool("mod-" + name + "-enabled", true); return state == ModState.enabled;
} }
public boolean hasUnmetDependencies(){ public boolean hasUnmetDependencies(){
@@ -760,37 +705,11 @@ public class Mods implements Loadable{
} }
} }
/** Thrown when an error occurs while loading a mod. public enum ModState{
public static class ModLoadException extends RuntimeException{ enabled,
public Content content; disabled,
public LoadedMod mod; missingDependencies,
unsupported,
public ModLoadException(String message, Throwable cause){ contentErrors
super(message, cause); }
}
public ModLoadException(String message, @Nullable Content content, Throwable cause){
super(message, cause);
this.content = content;
if(content != null){
this.mod = content.mod;
}
}
public ModLoadException(@Nullable Content content, Throwable cause){
super(cause);
this.content = content;
if(content != null){
this.mod = content.mod;
}
}
public ModLoadException(String message, @Nullable Content content){
super(message);
this.content = content;
if(content != null){
this.mod = content.mod;
}
}
}*/
} }

View File

@@ -75,7 +75,7 @@ public class ModsDialog extends FloatingDialog{
hidden(() -> { hidden(() -> {
if(mods.requiresReload()){ if(mods.requiresReload()){
ui.loadAnd("$reloading", () -> { ui.loadAnd("$reloading", () -> {
mods.all().each(mod -> { mods.eachEnabled(mod -> {
if(mod.hasUnmetDependencies()){ if(mod.hasUnmetDependencies()){
ui.showErrorMessage(Core.bundle.format("mod.nowdisabled", mod.name, mod.missingDependencies.toString(", "))); ui.showErrorMessage(Core.bundle.format("mod.nowdisabled", mod.name, mod.missingDependencies.toString(", ")));
} }
@@ -107,7 +107,7 @@ public class ModsDialog extends FloatingDialog{
cont.defaults().width(mobile ? 500 : 560f).pad(4); cont.defaults().width(mobile ? 500 : 560f).pad(4);
cont.add("$mod.reloadrequired").visible(mods::requiresReload).center().get().setAlignment(Align.center); cont.add("$mod.reloadrequired").visible(mods::requiresReload).center().get().setAlignment(Align.center);
cont.row(); cont.row();
if(!(mods.all().isEmpty() && mods.disabled().isEmpty())){ if(!mods.list().isEmpty()){
cont.pane(table -> { cont.pane(table -> {
table.margin(10f).top(); table.margin(10f).top();
Array<LoadedMod> all = Array.withArrays(mods.all(), mods.disabled()); Array<LoadedMod> all = Array.withArrays(mods.all(), mods.disabled());

View File

@@ -10,6 +10,7 @@ import io.anuke.arc.scene.style.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
@@ -461,6 +462,6 @@ public class PlacementFragment extends Fragment{
/** Returns the block currently being hovered over in the world. */ /** Returns the block currently being hovered over in the world. */
Block tileDisplayBlock(){ Block tileDisplayBlock(){
return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.drop() != null ? hoverTile.overlay().itemDrop != null ? hoverTile.overlay() : hoverTile.floor() : null; return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.drop() != null && hoverTile.block() == Blocks.air ? hoverTile.overlay().itemDrop != null ? hoverTile.overlay() : hoverTile.floor() : null;
} }
} }

View File

@@ -17,7 +17,6 @@ import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.desktop.steam.*; import io.anuke.mindustry.desktop.steam.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
@@ -195,12 +194,9 @@ public class DesktopLauncher extends ClientLauncher{
boolean fbgp = badGPU; boolean fbgp = badGPU;
CrashSender.send(e, file -> { CrashSender.send(e, file -> {
Array<Throwable> causes = Strings.getCauses(e); Throwable fc = Strings.getFinalCause(e);
Throwable fc = causes.find(t -> t instanceof ModLoadException);
if(fc == null) fc = Strings.getFinalCause(e);
Throwable cause = fc;
if(!fbgp){ if(!fbgp){
dialog.get(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + cause.getClass().getSimpleName().replace("Exception", "") + (cause.getMessage() == null ? "" : ":\n" + cause.getMessage()))); dialog.get(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + fc.getClass().getSimpleName().replace("Exception", "") + (fc.getMessage() == null ? "" : ":\n" + fc.getMessage())));
} }
}); });
} }

View File

@@ -25,7 +25,7 @@ public class ScriptStubGenerator{
Array<String> blacklist = Array.with("plugin", "mod", "net", "io", "tools"); Array<String> blacklist = Array.with("plugin", "mod", "net", "io", "tools");
Array<String> nameBlacklist = Array.with("ClientLauncher", "NetClient", "NetServer", "ClassAccess"); Array<String> nameBlacklist = Array.with("ClientLauncher", "NetClient", "NetServer", "ClassAccess");
Array<Class<?>> whitelist = Array.with(Draw.class, Fill.class, Lines.class, Core.class, TextureAtlas.class, TextureRegion.class, Time.class, System.class, PrintStream.class, Array<Class<?>> whitelist = Array.with(Draw.class, Fill.class, Lines.class, Core.class, TextureAtlas.class, TextureRegion.class, Time.class, System.class, PrintStream.class,
AtlasRegion.class, String.class, Mathf.class, Angles.class, Color.class, Runnable.class, Object.class, Icon.class, Tex.class, Sounds.class, Musics.class, Call.class); AtlasRegion.class, String.class, Mathf.class, Angles.class, Color.class, Runnable.class, Object.class, Icon.class, Tex.class, Sounds.class, Musics.class, Call.class, Texture.class, TextureData.class, Pixmap.class);
Array<String> nopackage = Array.with("java.lang", "java"); Array<String> nopackage = Array.with("java.lang", "java");
String fileTemplate = "package io.anuke.mindustry.mod;\n" + String fileTemplate = "package io.anuke.mindustry.mod;\n" +