1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-10-26 17:21:07 +03:00

Java mod import support / Moved mod browser button

This commit is contained in:
Anuken 2021-02-05 11:21:27 -05:00
parent 9b3360d932
commit fc3352bcb1
6 changed files with 210 additions and 122 deletions

View File

@ -17,21 +17,6 @@ jobs:
java-version: 14 java-version: 14
- name: Set env - name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Create artifacts
run: |
./gradlew desktop:dist server:dist core:javadoc -Pbuildversion=${RELEASE_VERSION:1}
- name: Update docs
run: |
cd ../
git config --global user.email "cli@github.com"
git config --global user.name "Github Actions"
git clone --depth=1 https://github.com/MindustryGame/docs.git
cp -a Mindustry/core/build/docs/javadoc/. docs/
cd docs
git add .
git commit -m "Update ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/MindustryGame/docs
cd ../Mindustry
- name: Add Arc release - name: Add Arc release
run: | run: |
git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc
@ -39,6 +24,24 @@ jobs:
git tag ${RELEASE_VERSION} git tag ${RELEASE_VERSION}
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION}; git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION};
cd ../Mindustry cd ../Mindustry
- name: Create artifacts
run: |
./gradlew desktop:dist server:dist core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
- name: Update docs
run: |
cd ../
git config --global user.email "cli@github.com"
git config --global user.name "Github Actions"
git clone --depth=1 https://github.com/MindustryGame/docs.git
cd docs
find . -maxdepth 1 ! -name ".git" ! -name . -exec rm -r {} \;
cd ../
cp -a Mindustry/core/build/javadoc/. docs/
cd docs
git add .
git commit -m "Update ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/MindustryGame/docs
cd ../Mindustry
- name: Update F-Droid build string - name: Update F-Droid build string
run: | run: |
git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds

View File

@ -335,6 +335,25 @@ project(":core"){
annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd' annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd'
} }
afterEvaluate{
task mergedJavadoc(type: Javadoc){
def blacklist = [project(":android"), project(":ios"), project(":desktop"), project(":server"), project(":annotations")]
source rootProject.subprojects.collect{ project ->
if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
return project.sourceSets.main.allJava
}
}
classpath = files(rootProject.subprojects.collect { project ->
if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
return project.sourceSets.main.compileClasspath
}
})
destinationDir = new File(buildDir, 'javadoc')
}
}
} }
project(":server"){ project(":server"){

View File

@ -41,7 +41,7 @@ be.ignore = Ignore
be.noupdates = No updates found. be.noupdates = No updates found.
be.check = Check for updates be.check = Check for updates
mod.featured.dialog.title = Mod Browser (WIP) mods.browser = Mod Browser
mods.browser.selected = Selected mod mods.browser.selected = Selected mod
mods.browser.add = Install mods.browser.add = Install
mods.browser.reinstall = Reinstall mods.browser.reinstall = Reinstall

View File

@ -50,8 +50,8 @@ public class Vars implements Loadable{
public static final Charset charset = Charset.forName("UTF-8"); public static final Charset charset = Charset.forName("UTF-8");
/** main application name, capitalized */ /** main application name, capitalized */
public static final String appName = "Mindustry"; public static final String appName = "Mindustry";
/** URL for itch.io donations. */ /** Github API URL. */
public static final String donationURL = "https://anuke.itch.io/mindustry/purchase"; public static final String ghApi = "https://api.github.com";
/** URL for discord invite. */ /** URL for discord invite. */
public static final String discordURL = "https://discord.gg/mindustry"; public static final String discordURL = "https://discord.gg/mindustry";
/** URL for sending crash reports to */ /** URL for sending crash reports to */

View File

@ -667,16 +667,22 @@ public class Mods implements Loadable{
Mod mainMod; Mod mainMod;
Fi mainFile = zip; Fi mainFile = zip;
String[] path = (mainClass.replace('.', '/') + ".class").split("/"); if(android){
for(String str : path){ mainFile = mainFile.child("classes.dex");
if(!str.isEmpty()){ }else{
mainFile = mainFile.child(str); String[] path = (mainClass.replace('.', '/') + ".class").split("/");
for(String str : path){
if(!str.isEmpty()){
mainFile = mainFile.child(str);
}
} }
} }
//make sure the main class exists before loading it; if it doesn't just don't put it there //make sure the main class exists before loading it; if it doesn't just don't put it there
if(mainFile.exists() && Core.settings.getBool("mod-" + baseName + "-enabled", true) && Version.isAtLeast(meta.minGameVersion) && meta.getMinMajor() >= 105){ //if the mod is explicitly marked as java, try loading it anyway
//mobile versions don't support class mods if((mainFile.exists() || meta.java) &&
Core.settings.getBool("mod-" + baseName + "-enabled", true) && Version.isAtLeast(meta.minGameVersion) && meta.getMinMajor() >= 105){
if(ios){ if(ios){
throw new IllegalArgumentException("Java class mods are not supported on iOS."); throw new IllegalArgumentException("Java class mods are not supported on iOS.");
} }
@ -746,6 +752,11 @@ public class Mods implements Loadable{
this.name = meta.name.toLowerCase().replace(" ", "-"); this.name = meta.name.toLowerCase().replace(" ", "-");
} }
/** @return whether this is a java class mod. */
public boolean isJava(){
return meta.java || main != null;
}
@Nullable @Nullable
public String getRepo(){ public String getRepo(){
return Core.settings.getString("mod-" + name + "-repo", meta.repo); return Core.settings.getString("mod-" + name + "-repo", meta.repo);
@ -867,6 +878,8 @@ public class Mods implements Loadable{
public Seq<String> dependencies = Seq.with(); public Seq<String> dependencies = Seq.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;
/** If true, this mod should be loaded as a Java class mod. This is technically optional, but highly recommended. */
public boolean java;
public String displayName(){ public String displayName(){
return displayName == null ? Strings.stripColors(name) : displayName; return displayName == null ? Strings.stripColors(name) : displayName;

View File

@ -42,6 +42,10 @@ public class ModsDialog extends BaseDialog{
buttons.button("@mods.guide", Icon.link, () -> Core.app.openURI(modGuideURL)).size(210, 64f); buttons.button("@mods.guide", Icon.link, () -> Core.app.openURI(modGuideURL)).size(210, 64f);
if(!mobile){
buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(modDirectory.absolutePath()));
}
shown(this::setup); shown(this::setup);
if(mobile){ if(mobile){
onResize(this::setup); onResize(this::setup);
@ -169,93 +173,15 @@ public class ModsDialog extends BaseDialog{
dialog.hide(); dialog.hide();
ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> { ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> {
//clean up the text in case somebody inputs a URL or adds random spaces
text = text.trim().replace(" ", "");
if(text.startsWith("https://github.com/")) text = text.substring("https://github.com/".length());
Core.settings.put("lastmod", text); Core.settings.put("lastmod", text);
//there's no good way to know if it's a java mod here, so assume it's not
githubImportMod(text); githubImportMod(text, false);
}); });
}).margin(12f); }).margin(12f);
t.row();
t.button("@mod.featured.dialog.title", Icon.star, bstyle, () -> {
Runnable[] rebuildBrowser = {null};
dialog.hide();
BaseDialog browser = new BaseDialog("$mod.featured.dialog.title");
browser.cont.table(table -> {
table.left();
table.image(Icon.zoom);
table.field(searchtxt, res -> {
searchtxt = res;
rebuildBrowser[0].run();
}).growX().get();
table.button(Icon.list, Styles.clearPartiali, 32f, () -> {
orderDate = !orderDate;
rebuildBrowser[0].run();
}).update(b -> b.getStyle().imageUp = (orderDate ? Icon.list : Icon.star)).size(40f).get()
.addListener(new Tooltip(tip -> tip.label(() -> orderDate ? "@mods.browser.sortdate" : "@mods.browser.sortstars").left()));
}).fillX().padBottom(4);
browser.cont.row();
browser.cont.pane(tablebrow -> {
tablebrow.margin(10f).top();
rebuildBrowser[0] = () -> {
tablebrow.clear();
tablebrow.add("@loading");
getModList(rlistings -> {
tablebrow.clear();
Seq<ModListing> listings = rlistings;
if(!orderDate){
listings = rlistings.copy();
listings.sortComparing(m1 -> -m1.stars);
}
for(ModListing mod : listings){
if((mod.hasJava && Vars.ios) || !searchtxt.isEmpty() && !mod.repo.toLowerCase().contains(searchtxt.toLowerCase()) || (Vars.ios && mod.hasScripts)) continue;
tablebrow.button(btn -> {
btn.top().left();
btn.margin(12f);
btn.table(con -> {
con.left();
con.add("[accent]" + mod.name + "[white]\n[lightgray]Author:[] " + mod.author + "\n[lightgray]\uE809 " + mod.stars +
(Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion)))
.width(388f).wrap().growX().pad(0f, 6f, 0f, 6f).left().labelAlign(Align.left);
con.add().growX().pad(0f, 6f, 0f, 6f);
}).fillY().growX().pad(0f, 6f, 0f, 6f);
}, Styles.modsb, () -> {
var sel = new BaseDialog(mod.name);
sel.cont.add(mod.description).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left);
sel.buttons.defaults().size(150f, 54f).pad(2f);
sel.setFillParent(false);
sel.buttons.button("@back", Icon.left, () -> {
sel.clear();
sel.hide();
});
var found = mods.list().find(l -> mod.repo != null && mod.repo.equals(l.getRepo()));
sel.buttons.button(found == null ? "@mods.browser.add" : "@mods.browser.reinstall", Icon.download, () -> {
sel.hide();
githubImportMod(mod.repo);
});
sel.buttons.button("@mods.github.open", Icon.link, () -> {
Core.app.openURI("https://github.com/" + mod.repo);
});
sel.keyDown(KeyCode.escape, sel::hide);
sel.keyDown(KeyCode.back, sel::hide);
sel.show();
}).width(460f).growX().left().fillY();
tablebrow.row();
}
});
};
rebuildBrowser[0].run();
}).get().setScrollingDisabled(true, false);
browser.addCloseButton();
browser.show();
}).margin(12f);
}); });
dialog.addCloseButton(); dialog.addCloseButton();
@ -263,9 +189,7 @@ public class ModsDialog extends BaseDialog{
}).margin(margin); }).margin(margin);
if(!mobile){ buttons.button("@mods.browser", Icon.menu, style, this::showModBrowser).margin(margin);
buttons.button("@mods.openfolder", Icon.link, style, () -> Core.app.openFolder(modDirectory.absolutePath())).margin(margin);
}
}).width(w); }).width(w);
cont.row(); cont.row();
@ -377,7 +301,7 @@ public class ModsDialog extends BaseDialog{
boolean showImport = !mod.hasSteamID(); boolean showImport = !mod.hasSteamID();
dialog.buttons.button("@mods.github.open", Icon.link, () -> Core.app.openURI("https://github.com/" + mod.getRepo())); dialog.buttons.button("@mods.github.open", Icon.link, () -> Core.app.openURI("https://github.com/" + mod.getRepo()));
if(mobile && showImport) dialog.buttons.row(); if(mobile && showImport) dialog.buttons.row();
if(showImport) dialog.buttons.button("@mods.browser.reinstall", Icon.download, () -> githubImportMod(mod.getRepo())); if(showImport) dialog.buttons.button("@mods.browser.reinstall", Icon.download, () -> githubImportMod(mod.getRepo(), mod.isJava()));
} }
//TODO improve this menu later //TODO improve this menu later
@ -432,6 +356,92 @@ public class ModsDialog extends BaseDialog{
dialog.show(); dialog.show();
} }
private void showModBrowser(){
Runnable[] rebuildBrowser = {null};
BaseDialog browser = new BaseDialog("@mods.browser");
browser.cont.table(table -> {
table.left();
table.image(Icon.zoom);
table.field(searchtxt, res -> {
searchtxt = res;
rebuildBrowser[0].run();
}).growX().get();
table.button(Icon.list, Styles.clearPartiali, 32f, () -> {
orderDate = !orderDate;
rebuildBrowser[0].run();
}).update(b -> b.getStyle().imageUp = (orderDate ? Icon.list : Icon.star)).size(40f).get()
.addListener(new Tooltip(tip -> tip.label(() -> orderDate ? "@mods.browser.sortdate" : "@mods.browser.sortstars").left()));
}).fillX().padBottom(4);
browser.cont.row();
browser.cont.pane(tablebrow -> {
tablebrow.margin(10f).top();
rebuildBrowser[0] = () -> {
tablebrow.clear();
tablebrow.add("@loading");
getModList(rlistings -> {
tablebrow.clear();
Seq<ModListing> listings = rlistings;
if(!orderDate){
listings = rlistings.copy();
listings.sortComparing(m1 -> -m1.stars);
}
for(ModListing mod : listings){
if((mod.hasJava && Vars.ios) || !searchtxt.isEmpty() && !mod.repo.toLowerCase().contains(searchtxt.toLowerCase()) || (Vars.ios && mod.hasScripts)) continue;
tablebrow.button(btn -> {
btn.top().left();
btn.margin(12f);
btn.table(con -> {
con.left();
con.add("[accent]" + mod.name + "[white]\n[lightgray]Author:[] " + trimText(mod.author) + "\n[lightgray]\uE809 " + mod.stars +
(Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion)))
.width(388f).wrap().growX().pad(0f, 6f, 0f, 6f).left().labelAlign(Align.left);
con.add().growX().pad(0f, 6f, 0f, 6f);
}).fillY().growX().pad(0f, 6f, 0f, 6f);
}, Styles.modsb, () -> {
var sel = new BaseDialog(mod.name);
sel.cont.add(mod.description).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left);
sel.buttons.defaults().size(150f, 54f).pad(2f);
sel.setFillParent(false);
sel.buttons.button("@back", Icon.left, () -> {
sel.clear();
sel.hide();
});
var found = mods.list().find(l -> mod.repo != null && mod.repo.equals(l.getRepo()));
sel.buttons.button(found == null ? "@mods.browser.add" : "@mods.browser.reinstall", Icon.download, () -> {
sel.hide();
githubImportMod(mod.repo, mod.hasJava);
});
sel.buttons.button("@mods.github.open", Icon.link, () -> {
Core.app.openURI("https://github.com/" + mod.repo);
});
sel.keyDown(KeyCode.escape, sel::hide);
sel.keyDown(KeyCode.back, sel::hide);
sel.show();
}).width(460f).growX().left().fillY();
tablebrow.row();
}
});
};
rebuildBrowser[0].run();
}).get().setScrollingDisabled(true, false);
browser.addCloseButton();
browser.show();
}
private String trimText(String text){
if(text.contains("\n")){
return text.substring(0, text.indexOf("\n"));
}
return text;
}
private void handleMod(String repo, HttpResponse result){ private void handleMod(String repo, HttpResponse result){
try{ try{
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip"); Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
@ -452,15 +462,58 @@ public class ModsDialog extends BaseDialog{
} }
} }
private void githubImportMod(String name){ private void importFail(Throwable t){
ui.loadfrag.show(); Core.app.post(() -> modError(t));
Core.net.httpGet("https://api.github.com/repos/" + name, res -> { }
if(checkError(res)){
String mainBranch = Jval.read(res.getResultAsString()).getString("default_branch");
githubImportBranch(mainBranch, name, this::showStatus); private void githubImportMod(String repo, boolean isJava){
ui.loadfrag.show();
if(isJava){
githubImportJavaMod(repo);
}else{
Core.net.httpGet(ghApi + "/repos/" + repo, res -> {
if(checkError(res)){
var json = Jval.read(res.getResultAsString());
String mainBranch = json.getString("default_branch");
String language = json.getString("language", "<none>");
//this is a crude heuristic for class mods; only required for direct github import
//TODO make a more reliable way to distinguish java mod repos
if(language.equals("Java") || language.equals("Kotlin")){
githubImportJavaMod(repo);
}else{
githubImportBranch(mainBranch, repo, this::showStatus);
}
}
}, this::importFail);
}
}
private void githubImportJavaMod(String repo){
//grab latest release
Core.net.httpGet(ghApi + "/repos/" + repo + "/releases/latest", res -> {
if(checkError(res)){
var json = Jval.read(res.getResultAsString());
var assets = json.get("assets").asArray();
//prioritize dexed jar, as that's what Sonnicon's mod template outputs
var dexedAsset = assets.find(j -> j.getString("name").startsWith("dexed") && j.getString("name").endsWith(".jar"));
var asset = dexedAsset == null ? assets.find(j -> j.getString("name").endsWith(".jar")) : dexedAsset;
if(asset != null){
//grab actual file
var url = asset.getString("browser_download_url");
Core.net.httpGet(url, result -> {
if(checkError(result)){
handleMod(repo, result);
}
}, this::importFail);
}else{
throw new ArcRuntimeException("No JAR file found in releases. Make sure you have a valid jar file in the mod's latest Github Release.");
}
} }
}, t -> Core.app.post(() -> modError(t))); }, this::importFail);
} }
private boolean checkError(HttpResponse res){ private boolean checkError(HttpResponse res){
@ -480,7 +533,7 @@ public class ModsDialog extends BaseDialog{
} }
private void githubImportBranch(String branch, String repo, Cons<HttpStatus> err){ private void githubImportBranch(String branch, String repo, Cons<HttpStatus> err){
Core.net.httpGet("https://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> { Core.net.httpGet(ghApi + "/repos/" + repo + "/zipball/" + branch, loc -> {
if(loc.getStatus() == HttpStatus.OK){ if(loc.getStatus() == HttpStatus.OK){
if(loc.getHeader("Location") != null){ if(loc.getHeader("Location") != null){
Core.net.httpGet(loc.getHeader("Location"), result -> { Core.net.httpGet(loc.getHeader("Location"), result -> {
@ -489,13 +542,13 @@ public class ModsDialog extends BaseDialog{
}else{ }else{
handleMod(repo, result); handleMod(repo, result);
} }
}, t2 -> Core.app.post(() -> modError(t2))); }, this::importFail);
}else{ }else{
handleMod(repo, loc); handleMod(repo, loc);
} }
}else{ }else{
err.get(loc.getStatus()); err.get(loc.getStatus());
} }
}, t2 -> Core.app.post(() -> modError(t2))); }, this::importFail);
} }
} }