Skip to content

Commit

Permalink
Better mod updating:
Browse files Browse the repository at this point in the history
- No longer keeps multiple copies of mods on windows (vanilla issue)
- Autoupdater no longer loads the newly downloaded copy of the mod until the game is restarted.
  • Loading branch information
buthed010203 committed Nov 4, 2024
1 parent f0af61e commit 5742491
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 22 deletions.
78 changes: 58 additions & 20 deletions core/src/mindustry/mod/Mods.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import mindustry.ui.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;

Expand Down Expand Up @@ -114,18 +115,21 @@ public LoadedMod importMod(Fi file) throws IOException{
file.copyTo(dest);

var loaded = loadMod(dest, true, true);
mods.add(loaded);
//invalidate ordered mods cache
lastOrderedMods = null;
requiresReload = true;
//enable the mod on import
Core.settings.put("mod-" + loaded.name + "-enabled", true);
sortMods();
if(!loaded.isAutoUpdating) { // If this was imported as an auto update, don't run any code relating to importing the mod.
mods.add(loaded);
//invalidate ordered mods cache
lastOrderedMods = null;
requiresReload = true;
//enable the mod on import
Core.settings.put("mod-" + loaded.name + "-enabled", true);
sortMods();
}
//try to load the mod's icon so it displays on import
Core.app.post(() -> loadIcon(loaded));

Events.fire(Trigger.importMod);

loaded.isAutoUpdating = false; // No longer auto updating
return loaded;
}catch(IOException e){
dest.delete();
Expand Down Expand Up @@ -978,6 +982,19 @@ private LoadedMod loadMod(Fi sourceFile) throws Exception{
return loadMod(sourceFile, false, true);
}

private final ObjectMap<String, Runnable> autoUpdatedMods = new ObjectMap<>();
{ // This is jank. I do not mind.
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (autoUpdatedMods.isEmpty()) return;
Log.debug("Installing updated mods.");
autoUpdatedMods.each((mod, run) -> {
if (mods.contains(m -> m.name.equals(mod))) run.run();
else Log.info("Failed to find mod @", mod);
});
Log.debug("Updated mod installation succeeded.");
}));
}

/** 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, boolean initialize) throws Exception{
Expand Down Expand Up @@ -1008,18 +1025,33 @@ private LoadedMod loadMod(Fi sourceFile, boolean overwrite, boolean initialize)
if(other != null){
//steam mods can't really be deleted, they need to be unsubscribed
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);
Runnable handleOverwrite = () -> {
//close the classloader for jar mods
if(other.loader instanceof URLClassLoader c){
try{
c.close();
}catch(IOException e){
throw new RuntimeException(e);
}
}

//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);
};
if(other.isAutoUpdating) { // If we're auto updating, overwrite on shutdown.
autoUpdatedMods.put(baseName, handleOverwrite);
initialize = false;
} else handleOverwrite.run(); // If we're not auto updating, overwrite immediately.
}else{
throw new ModLoadException("A mod with the name '" + baseName + "' is already imported.");
}
Expand Down Expand Up @@ -1097,7 +1129,10 @@ private LoadedMod loadMod(Fi sourceFile, boolean overwrite, boolean initialize)
Log.info("Loaded mod '@' in @ms", meta.name, duration);
}

return new LoadedMod(sourceFile, zip, mainMod, loader, meta);
var out = new LoadedMod(sourceFile, zip, mainMod, loader, meta);
out.isAutoUpdating = other != null && other.isAutoUpdating; // Propagate autoUpdating value to output
if(other != null) other.isAutoUpdating = false; // Other is no longer being updated
return out;
}catch(Exception e){
//delete root zip file so it can be closed on windows
if(rootZip != null) rootZip.delete();
Expand Down Expand Up @@ -1133,6 +1168,9 @@ public static class LoadedMod implements Publishable, Disposable{

/** Foo's addition to track whether we have tried to load this mod's icon */
public boolean attemptedIconLoad;
/** Whether this mod is currently being auto updated. */
public boolean isAutoUpdating;

private static final boolean iconLoadingOptimization = Core.settings.getBool("modiconloadingoptimization");
private static final ObjectSet<String> iconDeferralUnsupported = ObjectSet.with("mi2-utilities-java", "olupis");

Expand Down
4 changes: 2 additions & 2 deletions core/src/mindustry/ui/dialogs/ModsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ public ModsDialog(){
for (Mods.LoadedMod mod : mods.mods.copy().shuffle()) { // Use shuffled mod list, if the user has more than 30 active mods, this will ensure that each is checked at least somewhat frequently FINISHME: This should take dependencies and requirements into account which we don't do currently
if (!mod.enabled() || mod.getRepo() == null || !settings.getBool(mod.autoUpdateString(), true)) continue;
if (expected++ >= 30) continue; // Only make up to 30 api requests
onSuccess.put(mod.getRepo(), autoUpdaterHandler);
githubImportMod(mod.getRepo(), mod.isJava(), null, mod.meta.version); // FINISHME: Handle deletion of old file on shutdown
mod.isAutoUpdating = true;
githubImportMod(mod.getRepo(), mod.isJava(), null, mod.meta.version, autoUpdaterHandler);
}
} else Log.debug("Not updating mods, updated too recently / auto update is disabled / no enabled mods.");
});
Expand Down

0 comments on commit 5742491

Please sign in to comment.