Added mod dependencies

This commit is contained in:
Anuken
2019-10-27 18:07:18 -04:00
parent ef2817513a
commit 9e4e58baf9
3 changed files with 73 additions and 8 deletions

View File

@@ -94,6 +94,8 @@ mods.report = Report Bug
mod.enabled = [lightgray]Enabled mod.enabled = [lightgray]Enabled
mod.disabled = [scarlet]Disabled mod.disabled = [scarlet]Disabled
mod.disable = Disable mod.disable = Disable
mod.missingdependencies = [scarlet]Missing dependencies: {0}
mod.nowdisabled = [scarlet]Mod '{0}' is missing dependencies:[accent] {1}\n[lightgray]These mods need to be downloaded first.\nThis mod will be automatically disabled.
mod.enable = Enable mod.enable = Enable
mod.requiresrestart = The game will now close to apply the mod changes. mod.requiresrestart = The game will now close to apply the mod changes.
mod.reloadrequired = [scarlet]Reload Required mod.reloadrequired = [scarlet]Reload Required

View File

@@ -200,14 +200,61 @@ public class Mods implements Loadable{
} }
} }
resolveDependencies();
//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)); loaded.sort(Structs.comparing(m -> m.name));
buildFiles(); buildFiles();
} }
private void buildFiles(){ private void resolveDependencies(){
for(LoadedMod mod : Array.<LoadedMod>withArrays(loaded, disabled)){
updateDependencies(mod);
}
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){
mod.dependencies.clear();
mod.missingDependencies.clear();
mod.dependencies = mod.meta.dependencies.map(this::locateMod);
for(int i = 0; i < mod.dependencies.size; i++){
if(mod.dependencies.get(i) == null){
mod.missingDependencies.add(mod.meta.dependencies.get(i));
}
}
}
private void topoSort(LoadedMod mod, Array<LoadedMod> stack, ObjectSet<LoadedMod> visited){
visited.add(mod);
mod.dependencies.each(m -> !visited.contains(m), m -> topoSort(m, stack, visited));
stack.add(mod);
}
/** @return mods ordered in the correct way needed for dependencies. */
private Array<LoadedMod> orderedMods(){
ObjectSet<LoadedMod> visited = new ObjectSet<>();
Array<LoadedMod> result = new Array<>();
for(LoadedMod mod : loaded){ for(LoadedMod mod : loaded){
if(!visited.contains(mod)){
topoSort(mod, result, visited);
}
}
return result;
}
private LoadedMod locateMod(String name){
return loaded.find(mod -> mod.name.equals(name));
}
private void buildFiles(){
for(LoadedMod mod : orderedMods()){
boolean zipFolder = !mod.file.isDirectory() && mod.root.parent() != null; boolean zipFolder = !mod.file.isDirectory() && mod.root.parent() != null;
String parentName = zipFolder ? mod.root.name() : null; String parentName = zipFolder ? mod.root.name() : null;
for(FileHandle file : mod.root.list()){ for(FileHandle file : mod.root.list()){
@@ -256,7 +303,6 @@ public class Mods implements Loadable{
loaded.clear(); loaded.clear();
disabled.clear(); disabled.clear();
load(); load();
buildFiles();
Sounds.dispose(); Sounds.dispose();
Sounds.load(); Sounds.load();
Core.assets.finishLoading(); Core.assets.finishLoading();
@@ -275,8 +321,7 @@ public class Mods implements Loadable{
/** Creates all the content found in mod files. */ /** Creates all the content found in mod files. */
public void loadContent(){ public void loadContent(){
for(LoadedMod mod : orderedMods()){
for(LoadedMod mod : loaded){
safeRun(mod, () -> { safeRun(mod, () -> {
if(mod.root.child("content").exists()){ if(mod.root.child("content").exists()){
FileHandle contentRoot = mod.root.child("content"); FileHandle contentRoot = mod.root.child("content");
@@ -336,11 +381,13 @@ public class Mods implements Loadable{
requiresReload = true; requiresReload = true;
if(!enabled){ if(!enabled){
loaded.remove(mod); loaded.remove(mod);
disabled.add(mod); if(!disabled.contains(mod)) disabled.add(mod);
}else{ }else{
loaded.add(mod); if(!loaded.contains(mod)) loaded.add(mod);
disabled.remove(mod); disabled.remove(mod);
} }
loaded.each(this::updateDependencies);
disabled.each(this::updateDependencies);
} }
} }
@@ -465,6 +512,10 @@ public class Mods implements Loadable{
public final String name; public final String name;
/** This mod's metadata. */ /** This mod's metadata. */
public final ModMeta meta; public final ModMeta meta;
/** This mod's dependencies as already-loaded mods. */
public Array<LoadedMod> dependencies = new Array<>();
/** All missing dependencies of this mod as strings. */
public Array<String> missingDependencies = new Array<>();
public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){ public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){
this.root = root; this.root = root;
@@ -478,6 +529,10 @@ public class Mods implements Loadable{
return Core.settings.getBool("mod-" + name + "-enabled", true); return Core.settings.getBool("mod-" + name + "-enabled", true);
} }
public boolean hasUnmetDependencies(){
return !missingDependencies.isEmpty();
}
@Override @Override
public String getSteamID(){ public String getSteamID(){
return Core.settings.getString(name + "-steamid", null); return Core.settings.getString(name + "-steamid", null);
@@ -548,7 +603,7 @@ public class Mods implements Loadable{
/** Plugin metadata information.*/ /** Plugin metadata information.*/
public static class ModMeta{ public static class ModMeta{
public String name, author, description, version, main; public String name, author, description, version, main;
public String[] dependencies = {}; //TODO implement public Array<String> dependencies = Array.with();
/** Hidden mods are only server-side or client-side, and do not support adding new content. */ /** Hidden mods are only server-side or client-side, and do not support adding new content. */
public boolean hidden; public boolean hidden;
} }

View File

@@ -70,6 +70,11 @@ public class ModsDialog extends FloatingDialog{
hidden(() -> { hidden(() -> {
if(mods.requiresReload()){ if(mods.requiresReload()){
ui.loadAnd("$reloading", () -> { ui.loadAnd("$reloading", () -> {
mods.all().each(mod -> {
if(mod.hasUnmetDependencies()){
ui.showErrorMessage(Core.bundle.format("mod.nowdisabled", mod.name, mod.missingDependencies.toString(", ")));
}
});
mods.reloadContent(); mods.reloadContent();
}); });
} }
@@ -141,7 +146,10 @@ public class ModsDialog extends FloatingDialog{
t.labelWrap("[lightgray]" + mod.meta.description).growX(); t.labelWrap("[lightgray]" + mod.meta.description).growX();
t.row(); t.row();
} }
if(mod.hasUnmetDependencies()){
t.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
t.row();
}
}).width(500f); }).width(500f);
table.row(); table.row();
} }