Allow mod re-import / Save mod repo on import
This commit is contained in:
@@ -44,7 +44,8 @@ be.check = Check for updates
|
|||||||
mod.featured.dialog.title = Mod Browser (WIP)
|
mod.featured.dialog.title = Mod Browser (WIP)
|
||||||
mods.browser.selected = Selected mod
|
mods.browser.selected = Selected mod
|
||||||
mods.browser.add = Install
|
mods.browser.add = Install
|
||||||
mods.github.open = View
|
mods.browser.reinstall = Reinstall
|
||||||
|
mods.github.open = Repo
|
||||||
mods.browser.sortdate = Sort by recent
|
mods.browser.sortdate = Sort by recent
|
||||||
mods.browser.sortstars = Sort by stars
|
mods.browser.sortstars = Sort by stars
|
||||||
|
|
||||||
|
|||||||
@@ -363,6 +363,16 @@ public class UI implements ApplicationListener, Loadable{
|
|||||||
}}.show();
|
}}.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showInfoOnHidden(String info, Runnable listener){
|
||||||
|
new Dialog(""){{
|
||||||
|
getCell(cont).growX();
|
||||||
|
cont.margin(15).add(info).width(400f).wrap().get().setAlignment(Align.center, Align.center);
|
||||||
|
buttons.button("@ok", this::hide).size(110, 50).pad(4);
|
||||||
|
hidden(listener);
|
||||||
|
closeOnBack();
|
||||||
|
}}.show();
|
||||||
|
}
|
||||||
|
|
||||||
public void showStartupInfo(String info){
|
public void showStartupInfo(String info){
|
||||||
new Dialog(""){{
|
new Dialog(""){{
|
||||||
getCell(cont).growX();
|
getCell(cont).growX();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package mindustry.mod;
|
|||||||
public class ModListing{
|
public class ModListing{
|
||||||
public String repo, name, author, lastUpdated, description, minGameVersion;
|
public String repo, name, author, lastUpdated, description, minGameVersion;
|
||||||
public boolean hasScripts, hasJava;
|
public boolean hasScripts, hasJava;
|
||||||
|
public String[] contentTypes = {};
|
||||||
public int stars;
|
public int stars;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -65,23 +65,39 @@ public class Mods implements Loadable{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return the loaded mod found by name, or null if not found. */
|
||||||
|
public @Nullable LoadedMod getMod(String name){
|
||||||
|
return mods.find(m -> m.name.equals(name));
|
||||||
|
}
|
||||||
|
|
||||||
/** @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 mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);
|
return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Imports an external mod file.*/
|
/** Imports an external mod file. Folders are not supported here. */
|
||||||
public void importMod(Fi file) throws IOException{
|
public LoadedMod importMod(Fi file) throws IOException{
|
||||||
Fi dest = modDirectory.child(file.name() + (file.extension().isEmpty() ? ".zip" : ""));
|
String baseName = file.nameWithoutExtension();
|
||||||
if(dest.exists()){
|
String finalName = baseName;
|
||||||
throw new IOException("A file with the same name already exists in the mod folder!");
|
//find a name to prevent any name conflicts
|
||||||
|
int count = 1;
|
||||||
|
while(modDirectory.child(finalName + ".zip").exists()){
|
||||||
|
finalName = baseName + "" + count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fi dest = modDirectory.child(finalName + ".zip");
|
||||||
|
|
||||||
file.copyTo(dest);
|
file.copyTo(dest);
|
||||||
try{
|
try{
|
||||||
mods.add(loadMod(dest));
|
var loaded = loadMod(dest, true);
|
||||||
|
mods.add(loaded);
|
||||||
requiresReload = true;
|
requiresReload = true;
|
||||||
sortMods();
|
sortMods();
|
||||||
|
//try to load the mod's icon so it displays on import
|
||||||
|
Core.app.post(() -> {
|
||||||
|
loadIcon(loaded);
|
||||||
|
});
|
||||||
|
return loaded;
|
||||||
}catch(IOException e){
|
}catch(IOException e){
|
||||||
dest.delete();
|
dest.delete();
|
||||||
throw e;
|
throw e;
|
||||||
@@ -113,13 +129,18 @@ public class Mods implements Loadable{
|
|||||||
|
|
||||||
private void loadIcons(){
|
private void loadIcons(){
|
||||||
for(LoadedMod mod : mods){
|
for(LoadedMod mod : mods){
|
||||||
//try to load icon for each mod that can have one
|
loadIcon(mod);
|
||||||
if(mod.root.child("icon.png").exists()){
|
}
|
||||||
try{
|
}
|
||||||
mod.iconTexture = new Texture(mod.root.child("icon.png"));
|
|
||||||
}catch(Throwable t){
|
private void loadIcon(LoadedMod mod){
|
||||||
Log.err("Failed to load icon for mod '" + mod.name + "'.", t);
|
//try to load icon for each mod that can have one
|
||||||
}
|
if(mod.root.child("icon.png").exists()){
|
||||||
|
try{
|
||||||
|
mod.iconTexture = new Texture(mod.root.child("icon.png"));
|
||||||
|
mod.iconTexture.setFilter(TextureFilter.linear);
|
||||||
|
}catch(Throwable t){
|
||||||
|
Log.err("Failed to load icon for mod '" + mod.name + "'.", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -586,8 +607,14 @@ public class Mods implements Loadable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Loads a mod file+meta, but does not add it to the list.
|
/** Loads a mod file+meta, but does not add it to the list.
|
||||||
* Note that directories can be loaded as mods.*/
|
* Note that directories can be loaded as mods. */
|
||||||
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
private LoadedMod loadMod(Fi sourceFile) throws Exception{
|
||||||
|
return loadMod(sourceFile, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads a mod file+meta, but does not add it to the list.
|
||||||
|
* Note that directories can be loaded as mods. */
|
||||||
|
private LoadedMod loadMod(Fi sourceFile, boolean overwrite) throws Exception{
|
||||||
Time.mark();
|
Time.mark();
|
||||||
|
|
||||||
ZipFi rootZip = null;
|
ZipFi rootZip = null;
|
||||||
@@ -615,8 +642,25 @@ 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(mods.contains(m -> m.name.equals(baseName))){
|
var other = mods.find(m -> m.name.equals(baseName));
|
||||||
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
|
|
||||||
|
if(other != null){
|
||||||
|
if(overwrite && !other.hasSteamID()){
|
||||||
|
//close zip file
|
||||||
|
if(other.root instanceof ZipFi){
|
||||||
|
other.root.delete();
|
||||||
|
}
|
||||||
|
//delete the old mod directory
|
||||||
|
if(other.file.isDirectory()){
|
||||||
|
other.file.deleteDirectory();
|
||||||
|
}else{
|
||||||
|
other.file.delete();
|
||||||
|
}
|
||||||
|
//unload
|
||||||
|
mods.remove(other);
|
||||||
|
}else{
|
||||||
|
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mod mainMod;
|
Mod mainMod;
|
||||||
@@ -701,6 +745,15 @@ public class Mods implements Loadable{
|
|||||||
this.name = meta.name.toLowerCase().replace(" ", "-");
|
this.name = meta.name.toLowerCase().replace(" ", "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getRepo(){
|
||||||
|
return Core.settings.getString("mod-" + name + "-repo", meta.repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepo(String repo){
|
||||||
|
Core.settings.put("mod-" + name + "-repo", repo);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean enabled(){
|
public boolean enabled(){
|
||||||
return state == ModState.enabled || state == ModState.contentErrors;
|
return state == ModState.enabled || state == ModState.contentErrors;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ public class ModsDialog extends BaseDialog{
|
|||||||
ui.showErrorMessage(Core.bundle.format("connectfail", status));
|
ui.showErrorMessage(Core.bundle.format("connectfail", status));
|
||||||
}else{
|
}else{
|
||||||
try{
|
try{
|
||||||
modList = new Json().fromJson(Seq.class, ModListing.class, strResult);
|
var j = new Json();
|
||||||
|
j.setIgnoreUnknownFields(true);
|
||||||
|
modList = j.fromJson(Seq.class, ModListing.class, strResult);
|
||||||
|
|
||||||
var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
Func<String, Date> parser = text -> {
|
Func<String, Date> parser = text -> {
|
||||||
@@ -224,7 +226,9 @@ public class ModsDialog extends BaseDialog{
|
|||||||
sel.clear();
|
sel.clear();
|
||||||
sel.hide();
|
sel.hide();
|
||||||
});
|
});
|
||||||
sel.buttons.button("@mods.browser.add", Icon.download, () -> {
|
|
||||||
|
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();
|
sel.hide();
|
||||||
githubImportMod(mod.repo);
|
githubImportMod(mod.repo);
|
||||||
});
|
});
|
||||||
@@ -349,7 +353,7 @@ public class ModsDialog extends BaseDialog{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void reload(){
|
private void reload(){
|
||||||
ui.showInfo("@mods.reloadexit", () -> Core.app.exit());
|
ui.showInfoOnHidden("@mods.reloadexit", () -> Core.app.exit());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMod(LoadedMod mod){
|
private void showMod(LoadedMod mod){
|
||||||
@@ -361,6 +365,10 @@ public class ModsDialog extends BaseDialog{
|
|||||||
dialog.buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(mod.file.absolutePath()));
|
dialog.buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(mod.file.absolutePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mod.getRepo() != null){
|
||||||
|
dialog.buttons.button("@mods.github.open", Icon.link, () -> Core.app.openURI("https://github.com/" + mod.getRepo()));
|
||||||
|
}
|
||||||
|
|
||||||
//TODO improve this menu later
|
//TODO improve this menu later
|
||||||
dialog.cont.pane(desc -> {
|
dialog.cont.pane(desc -> {
|
||||||
desc.center();
|
desc.center();
|
||||||
@@ -406,8 +414,6 @@ public class ModsDialog extends BaseDialog{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +421,8 @@ public class ModsDialog extends BaseDialog{
|
|||||||
try{
|
try{
|
||||||
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
||||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||||
mods.importMod(file);
|
var mod = mods.importMod(file);
|
||||||
|
mod.setRepo(repo);
|
||||||
file.delete();
|
file.delete();
|
||||||
Core.app.post(() -> {
|
Core.app.post(() -> {
|
||||||
try{
|
try{
|
||||||
@@ -431,15 +438,28 @@ public class ModsDialog extends BaseDialog{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void githubImportMod(String name){
|
private void githubImportMod(String name){
|
||||||
//try several branches
|
Core.net.httpGet("https://api.github.com/repos/" + name, res -> {
|
||||||
//TODO use only the main branch as specified in meta
|
if(checkError(res)){
|
||||||
githubImportBranch("6.0", name, e1 -> {
|
String mainBranch = Jval.read(res.getResultAsString()).getString("default_branch");
|
||||||
githubImportBranch("master", name, e2 -> {
|
|
||||||
githubImportBranch("main", name, e3 -> {
|
githubImportBranch(mainBranch, name, this::showStatus);
|
||||||
ui.showErrorMessage(Core.bundle.format("connectfail", e2));
|
}
|
||||||
ui.loadfrag.hide();
|
}, t -> Core.app.post(() -> modError(t)));
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
private boolean checkError(HttpResponse res){
|
||||||
|
if(res.getStatus() == HttpStatus.OK){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
showStatus(res.getStatus());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showStatus(HttpStatus status){
|
||||||
|
Core.app.post(() -> {
|
||||||
|
ui.showErrorMessage(Core.bundle.format("connectfail", Strings.capitalize(status.toString().toLowerCase())));
|
||||||
|
ui.loadfrag.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user