Merge branch 'mod-dependencies' of https://github.com/MEEPofFaith/Mindustry into MEEPofFaith-mod-dependencies
This commit is contained in:
@@ -2,7 +2,7 @@ package mindustry.mod;
|
||||
|
||||
/** Mod listing as a data class. */
|
||||
public class ModListing{
|
||||
public String repo, name, subtitle, author, lastUpdated, description, minGameVersion;
|
||||
public String repo, name, internalName, subtitle, author, lastUpdated, description, minGameVersion;
|
||||
public boolean hasScripts, hasJava;
|
||||
public String[] contentTypes = {};
|
||||
public int stars;
|
||||
@@ -12,6 +12,7 @@ public class ModListing{
|
||||
return "ModListing{" +
|
||||
"repo='" + repo + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", internalName='" + internalName + '\'' +
|
||||
", author='" + author + '\'' +
|
||||
", lastUpdated='" + lastUpdated + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
|
||||
@@ -50,6 +50,7 @@ public class Mods implements Loadable{
|
||||
private ModClassLoader mainLoader = new ModClassLoader(getClass().getClassLoader());
|
||||
|
||||
Seq<LoadedMod> mods = new Seq<>();
|
||||
private Seq<LoadedMod> newImports = new Seq<>();
|
||||
private ObjectMap<Class<?>, ModMeta> metas = new ObjectMap<>();
|
||||
private boolean requiresReload;
|
||||
|
||||
@@ -117,6 +118,7 @@ public class Mods implements Loadable{
|
||||
|
||||
var loaded = loadMod(dest, true, true);
|
||||
mods.add(loaded);
|
||||
newImports.add(loaded);
|
||||
//invalidate ordered mods cache
|
||||
lastOrderedMods = null;
|
||||
requiresReload = true;
|
||||
@@ -428,6 +430,7 @@ public class Mods implements Loadable{
|
||||
return;
|
||||
}
|
||||
mods.remove(mod);
|
||||
newImports.remove(mod);
|
||||
mod.dispose();
|
||||
if(mod.state != ModState.disabled){
|
||||
requiresReload = true;
|
||||
@@ -533,13 +536,20 @@ public class Mods implements Loadable{
|
||||
private void updateDependencies(LoadedMod mod){
|
||||
mod.dependencies.clear();
|
||||
mod.missingDependencies.clear();
|
||||
mod.missingSoftDependencies.clear();
|
||||
mod.dependencies = mod.meta.dependencies.map(this::locateMod);
|
||||
mod.softDependencies = mod.meta.softDependencies.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));
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < mod.softDependencies.size; i++){
|
||||
if(mod.softDependencies.get(i) == null){
|
||||
mod.missingSoftDependencies.add(mod.meta.softDependencies.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return mods ordered in the correct way needed for dependencies. */
|
||||
@@ -611,14 +621,13 @@ public class Mods implements Loadable{
|
||||
if(mods.contains(LoadedMod::hasContentErrors)){
|
||||
ui.loadfrag.hide();
|
||||
new Dialog(""){{
|
||||
|
||||
setFillParent(true);
|
||||
cont.margin(15);
|
||||
cont.add("@error.title");
|
||||
cont.row();
|
||||
cont.image().width(300f).pad(2).colspan(2).height(4f).color(Color.scarlet);
|
||||
cont.row();
|
||||
cont.add("@mod.errors").wrap().growX().center().get().setAlignment(Align.center);
|
||||
cont.add("@mod.errors").wrap().growX().center().labelAlign(Align.center);
|
||||
cont.row();
|
||||
cont.pane(p -> {
|
||||
mods.each(m -> m.enabled() && m.hasContentErrors(), m -> {
|
||||
@@ -649,6 +658,140 @@ public class Mods implements Loadable{
|
||||
cont.button("@ok", this::hide).size(300, 50);
|
||||
}}.show();
|
||||
}
|
||||
|
||||
//show list of missing dependencies
|
||||
Seq<LoadedMod> toCheck = mods.select(mod -> mod.shouldBeEnabled() && mod.hasUnmetDependencies());
|
||||
if(!toCheck.isEmpty()){
|
||||
ui.loadfrag.hide();
|
||||
checkDependencies(toCheck, false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Assume mods in toCheck are missing dependencies. */
|
||||
private void checkDependencies(Seq<LoadedMod> toCheck, boolean soft){
|
||||
new Dialog(""){{
|
||||
setFillParent(true);
|
||||
cont.margin(15);
|
||||
int span = soft ? 3 : 2;
|
||||
cont.add("@mod.dependencies.error").colspan(span);
|
||||
cont.row();
|
||||
cont.image().width(300f).colspan(span).pad(2).height(4f).color(Color.scarlet);
|
||||
cont.row();
|
||||
cont.pane(p -> {
|
||||
toCheck.each(mod -> {
|
||||
p.add(mod.meta.displayName).wrap().growX().left().labelAlign(Align.left);
|
||||
p.row();
|
||||
p.table(d -> {
|
||||
mod.missingDependencies.each(dep -> {
|
||||
d.add(dep).wrap().growX().left().labelAlign(Align.left);
|
||||
d.row();
|
||||
});
|
||||
if(soft){
|
||||
mod.missingSoftDependencies.each(dep -> {
|
||||
d.add(dep + " " + Core.bundle.get("mod.dependencies.soft")).wrap().growX().left().labelAlign(Align.left);
|
||||
d.row();
|
||||
});
|
||||
}
|
||||
}).growX().padBottom(8f).padLeft(12f);
|
||||
p.row();
|
||||
});
|
||||
}).fillX().colspan(span);
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.button("@cancel", this::hide).size(150, 50);
|
||||
cont.button(soft ? "@mod.dependencies.downloadreq" : "@mod.dependencies.download", () -> {
|
||||
hide();
|
||||
Seq<String> toImport = new Seq<>();
|
||||
toCheck.each(mod -> mod.missingDependencies.each(toImport::addUnique));
|
||||
downloadDependencies(toImport);
|
||||
}).size(150, 50);
|
||||
if(soft){
|
||||
cont.button("@mod.dependencies.downloadall", () -> {
|
||||
hide();
|
||||
Seq<String> toImport = new Seq<>();
|
||||
toCheck.each(mod -> mod.missingDependencies.each(toImport::addUnique));
|
||||
toCheck.each(mod -> mod.missingSoftDependencies.each(toImport::addUnique));
|
||||
downloadDependencies(toImport);
|
||||
}).size(150, 50);
|
||||
}
|
||||
}}.show();
|
||||
}
|
||||
|
||||
private void downloadDependencies(Seq<String> toImport){
|
||||
Seq<String> remaining = toImport.copy();
|
||||
ui.mods.importDependencies(remaining, () -> {
|
||||
toImport.removeAll(remaining);
|
||||
if(toImport.any()) requiresReload = true;
|
||||
displayDependencyImportStatus(remaining, toImport);
|
||||
});
|
||||
}
|
||||
|
||||
private void displayDependencyImportStatus(Seq<String> failed, Seq<String> success){
|
||||
new Dialog(""){{
|
||||
setFillParent(true);
|
||||
cont.margin(15);
|
||||
|
||||
cont.add("@mod.dependencies.status").center();
|
||||
cont.row();
|
||||
cont.image().width(300f).pad(2).height(4f).color(Color.lightGray);
|
||||
cont.row();
|
||||
|
||||
cont.pane(p -> {
|
||||
if(success.any()){
|
||||
p.add("@mod.dependencies.success").wrap().fillX().left().labelAlign(Align.left);
|
||||
p.row();
|
||||
p.table(t -> {
|
||||
success.each(d -> {
|
||||
t.add(d).wrap().growX().left().labelAlign(Align.left);
|
||||
t.row();
|
||||
});
|
||||
}).growX().padBottom(8f).padLeft(12f);
|
||||
p.row();
|
||||
}
|
||||
|
||||
if(failed.any()){
|
||||
p.add("@mod.dependencies.failure").wrap().fillX().left().labelAlign(Align.left);
|
||||
p.row();
|
||||
p.table(t -> {
|
||||
failed.each(d -> {
|
||||
t.add(d).wrap().growX().left().labelAlign(Align.left);
|
||||
t.row();
|
||||
});
|
||||
}).growX().padBottom(8f).padLeft(12f);
|
||||
}
|
||||
}).fillX();
|
||||
cont.row();
|
||||
|
||||
if(success.any()){
|
||||
cont.image().width(300f).pad(2).height(4f).color(Color.lightGray);
|
||||
cont.row();
|
||||
cont.add("@mods.reloadexit").center();
|
||||
cont.row();
|
||||
|
||||
hidden(() -> {
|
||||
Log.info("Exiting to reload mods after dependency auto-import.");
|
||||
Core.app.exit();
|
||||
});
|
||||
}
|
||||
|
||||
cont.button("@ok", this::hide).size(300, 50);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public void reload(){
|
||||
newImports.each(this::updateDependencies);
|
||||
newImports.remove(m -> m.missingDependencies.isEmpty() && m.softDependencies.isEmpty());
|
||||
|
||||
if(newImports.any()){
|
||||
checkDependencies(newImports, newImports.contains(m -> m.softDependencies.any()));
|
||||
}else{
|
||||
ui.showInfoOnHidden("@mods.reloadexit", () -> {
|
||||
Log.info("Exiting to reload mods.");
|
||||
Core.app.exit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasContentErrors(){
|
||||
@@ -1074,8 +1217,12 @@ public class Mods implements Loadable{
|
||||
public final ModMeta meta;
|
||||
/** This mod's dependencies as already-loaded mods. */
|
||||
public Seq<LoadedMod> dependencies = new Seq<>();
|
||||
/** All missing dependencies of this mod as strings. */
|
||||
/** This mod's soft dependencies as already-loaded mods. */
|
||||
public Seq<LoadedMod> softDependencies = new Seq<>();
|
||||
/** All missing required dependencies of this mod as strings. */
|
||||
public Seq<String> missingDependencies = new Seq<>();
|
||||
/** All missing soft dependencies of this mod as strings. */
|
||||
public Seq<String> missingSoftDependencies = new Seq<>();
|
||||
/** Content with initialization code. */
|
||||
public ObjectSet<Content> erroredContent = new ObjectSet<>();
|
||||
/** Current state of this mod. */
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ModsDialog extends BaseDialog{
|
||||
|
||||
hidden(() -> {
|
||||
if(mods.requiresReload()){
|
||||
reload();
|
||||
mods.reload();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -378,13 +378,6 @@ public class ModsDialog extends BaseDialog{
|
||||
return null;
|
||||
}
|
||||
|
||||
private void reload(){
|
||||
ui.showInfoOnHidden("@mods.reloadexit", () -> {
|
||||
Log.info("Exiting to reload mods.");
|
||||
Core.app.exit();
|
||||
});
|
||||
}
|
||||
|
||||
private void showMod(LoadedMod mod){
|
||||
BaseDialog dialog = new BaseDialog(mod.meta.displayName);
|
||||
|
||||
@@ -696,6 +689,16 @@ public class ModsDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
|
||||
public void importDependencies(Seq<String> dependencies, Runnable done){
|
||||
getModList(listings -> {
|
||||
listings.each(l -> dependencies.contains(l.internalName), l -> {
|
||||
dependencies.remove(l.internalName);
|
||||
githubImportMod(l.repo, l.hasJava);
|
||||
});
|
||||
done.run();
|
||||
});
|
||||
}
|
||||
|
||||
private void githubImportJavaMod(String repo, @Nullable String release){
|
||||
//grab latest release
|
||||
Http.get(ghApi + "/repos/" + repo + "/releases/" + (release == null ? "latest" : release), res -> {
|
||||
|
||||
Reference in New Issue
Block a user