-
-
Save erisdev/026796020d0b163d8d1bd737a7d4b82c to your computer and use it in GitHub Desktop.
dead fucking simple forge installer automation shim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dead fucking simple forge installer automation shim. | |
// compile : javac -cp forge-${version}-installer.jar ForgeHax.java | |
// run : java -cp .:forge-${version}-installer.jar ForgeHax -client FloraverseCommunityPack ~/.minecraft | |
import java.io.File; | |
import java.lang.reflect.Field; | |
// he he :3c | |
import net.minecraftforge.installer.actions.Action; | |
import net.minecraftforge.installer.actions.ActionCanceledException; | |
import net.minecraftforge.installer.actions.ClientInstall; | |
import net.minecraftforge.installer.actions.ProgressCallback; | |
import net.minecraftforge.installer.actions.ServerInstall; | |
import net.minecraftforge.installer.json.Install; | |
import net.minecraftforge.installer.json.Util; | |
class ForgeHax { | |
public static void main(String[] args) { | |
boolean serverInstall = false; | |
String profileName = null; | |
File installPath = null; | |
// absolute bare minimum naive command line option handling here lmao. of note is the fact that passing multiple -client and -server options is accepted, but only the last affects the outcome. also quietly ignore any excess arguments... it's fine because this isn't a user-facing tool | |
int i = 0; | |
while (i < args.length) { | |
String arg = args[i++]; | |
if (arg.equals("-client")) | |
serverInstall = false; | |
else if (arg.equals("-server")) | |
serverInstall = true; | |
else if (profileName == null) | |
profileName = arg; | |
else if (installPath == null) | |
installPath = new File(arg); | |
else break; | |
} | |
if (profileName == null) | |
die("Missing profile name and install path"); | |
if (installPath == null) | |
die("Missing install path"); | |
// this call loads the installation instructions from the forge installer jar | |
Install installProfile = Util.loadInstallProfile(); | |
// we want to change this to something modpack specific so we don't accidentally replace an existing forge install's profile. we don't bother with the icon or display name though because that stuff can be done by the launcher updater. this is literally the quickest, dirtiest hack there is. | |
setPrivate(Install.class, installProfile, "profile", profileName); | |
Action installer; | |
if (serverInstall) | |
installer = new ServerInstall(installProfile, ProgressCallback.TO_STD_OUT); | |
else | |
installer = new ClientInstall(installProfile, ProgressCallback.TO_STD_OUT); | |
// it pays to check this stuff ourselves because calling run with an invalid install path results in an ugly swing(?) dialog box and an apparent success from the caller's perspective. | |
if (!installer.isPathValid(installPath)) { | |
String errorMessage = installer.getFileError(installPath); | |
die(errorMessage); | |
} | |
try { | |
// run the installation process | |
installer.run(installPath, a -> true); | |
System.err.println(installer.getSuccessMessage()); | |
} | |
catch (ActionCanceledException err) { | |
// i'm not sure this can even happen | |
die("Installation was canceled"); | |
} | |
} | |
// just a convenience wrapper to print an error message and exit with a nonzero return code. | |
static void die(String message) { | |
System.err.println(message); | |
System.exit(1); | |
} | |
// hoo boy this is nasty, but it works!! | |
static <T> void setPrivate(Class cls, Object instance, String fieldName, T value) { | |
try { | |
Field field = cls.getDeclaredField(fieldName); | |
field.setAccessible(true); // ← right here ← this way | |
field.set(instance, value); // boom, fuck encapsulation | |
} | |
// i promise the one field i am using this for actually exists | |
catch (NoSuchFieldException fuckYourCheckedExceptions) {} | |
// I PROMISE I DID THE MAGIC INVOCATION TO MAKE THIS NOT HAPPEN, LOOK ↑ | |
catch (IllegalAccessException why) {} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment