Skip to content

Instantly share code, notes, and snippets.


erisdev/ Secret

Created Dec 24, 2019
What would you like to do?
dead fucking simple forge installer automation shim
// dead fucking simple forge installer automation shim.
// compile : javac -cp forge-${version}-installer.jar
// run : java -cp .:forge-${version}-installer.jar ForgeHax -client FloraverseCommunityPack ~/.minecraft
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);
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);
try {
// run the installation process, a -> true);
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) {
// 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) {}
catch (IllegalAccessException why) {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.