Fully automated Forge version list and installation

LexManos may feel free to fuck off ngl
Also: makes the Arc DNS injector and the Forge installer separate components instead of non-overwritable assets, so that we can actually update them on the user's end
This commit is contained in:
BuildTools 2023-06-25 14:27:48 +03:00
parent b263a9f6ce
commit 30ec1e2df8
25 changed files with 634 additions and 9 deletions

View File

@ -1 +1 @@
1678189863424 1687691695259

View File

@ -590,11 +590,15 @@ public final class Tools {
} }
public static void dialogOnUiThread(final Activity activity, final CharSequence title, final CharSequence message) { public static void dialogOnUiThread(final Activity activity, final CharSequence title, final CharSequence message) {
activity.runOnUiThread(() -> new AlertDialog.Builder(activity) activity.runOnUiThread(()->dialog(activity, title, message));
}
public static void dialog(final Context context, final CharSequence title, final CharSequence message) {
new AlertDialog.Builder(context)
.setTitle(title) .setTitle(title)
.setMessage(message) .setMessage(message)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show()); .show();
} }
public static void openURL(Activity act, String url) { public static void openURL(Activity act, String url) {

View File

@ -3,10 +3,9 @@ package net.kdt.pojavlaunch.fragments;
import static net.kdt.pojavlaunch.Tools.shareLog; import static net.kdt.pojavlaunch.Tools.shareLog;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.DocumentsContract;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.Toast; import android.widget.Toast;
@ -15,12 +14,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.CustomControlsActivity; import net.kdt.pojavlaunch.CustomControlsActivity;
import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.extra.ExtraConstants; import net.kdt.pojavlaunch.extra.ExtraConstants;
import net.kdt.pojavlaunch.extra.ExtraCore; import net.kdt.pojavlaunch.extra.ExtraCore;
import net.kdt.pojavlaunch.modloaders.ForgeDownloaderDialog;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
public class MainMenuFragment extends Fragment { public class MainMenuFragment extends Fragment {
@ -52,6 +51,11 @@ public class MainMenuFragment extends Fragment {
mPlayButton.setOnClickListener(v -> ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true)); mPlayButton.setOnClickListener(v -> ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true));
mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext())); mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));
mNewsButton.setOnLongClickListener((v)->{
new ForgeDownloaderDialog().show(view.getContext(), (ViewGroup) view);
return true;
});
} }
private void runInstallerWithConfirmation(boolean isCustomArgs) { private void runInstallerWithConfirmation(boolean isCustomArgs) {
if (ProgressKeeper.getTaskCount() == 0) if (ProgressKeeper.getTaskCount() == 0)

View File

@ -0,0 +1,7 @@
package net.kdt.pojavlaunch.modloaders;
public interface ForgeDownloadListener {
void onDownloadFinished();
void onInstallerNotAvailable();
void onDownloadError(Exception e);
}

View File

@ -0,0 +1,48 @@
package net.kdt.pojavlaunch.modloaders;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ForgeDownloadTask implements Runnable, Tools.DownloaderFeedback {
private final String mForgeUrl;
private final String mForgeVersion;
private final File mDestinationFile;
private final ForgeDownloadListener mListener;
public ForgeDownloadTask(ForgeDownloadListener listener, String forgeVersion, File destinationFile) {
this.mListener = listener;
this.mForgeUrl = ForgeUtils.getInstallerUrl(forgeVersion);
this.mForgeVersion = forgeVersion;
this.mDestinationFile = destinationFile;
}
@Override
public void run() {
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.forge_dl_progress, mForgeVersion);
try {
byte[] buffer = new byte[8192];
DownloadUtils.downloadFileMonitored(mForgeUrl, mDestinationFile, buffer, this);
mListener.onDownloadFinished();
}catch (IOException e) {
if(e instanceof FileNotFoundException) {
mListener.onInstallerNotAvailable();
}else{
mListener.onDownloadError(e);
}
}
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, -1, -1);
}
@Override
public void updateProgress(int curr, int max) {
int progress100 = (int)(((float)curr / (float)max)*100f);
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.forge_dl_progress, mForgeVersion);
}
}

View File

@ -0,0 +1,94 @@
package net.kdt.pojavlaunch.modloaders;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.ProgressBar;
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class ForgeDownloaderDialog implements Runnable, ExpandableListView.OnChildClickListener, ForgeDownloadListener {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private ExpandableListView mExpandableListView;
private ProgressBar mProgressBar;
private AlertDialog mAlertDialog;
private File mDestinationFile;
private Context mContext;
public void show(Context context, ViewGroup root) {
this.mContext = context;
this.mDestinationFile = new File(context.getCacheDir(), "forge-installer.jar");
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_expandable_forge_list, root, false);
mProgressBar = dialogView.findViewById(R.id.forge_list_progress_bar);
mExpandableListView = dialogView.findViewById(R.id.forge_expandable_version_list);
mExpandableListView.setOnChildClickListener(this);
dialogBuilder.setView(dialogView);
mAlertDialog = dialogBuilder.show();
new Thread(this).start();
}
@Override
public void run() {
try {
List<String> forgeVersions = ForgeUtils.downloadForgeVersions();
mHandler.post(()->{
if(forgeVersions != null) {
mProgressBar.setVisibility(View.GONE);
mExpandableListView.setAdapter(new ForgeVersionListAdapter(forgeVersions, LayoutInflater.from(mContext)));
}else{
mAlertDialog.dismiss();
}
});
}catch (IOException e) {
mHandler.post(()->{
mAlertDialog.dismiss();
Tools.showError(mContext, e);
});
}
}
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {
String forgeVersion = (String)expandableListView.getExpandableListAdapter().getChild(i, i1);
new Thread(new ForgeDownloadTask(this, forgeVersion, mDestinationFile)).start();
mAlertDialog.dismiss();
return true;
}
@Override
public void onDownloadFinished() {
Intent intent = new Intent(mContext, JavaGUILauncherActivity.class);
ForgeUtils.addAutoInstallArgs(intent, mDestinationFile, true); // since it's a user-invoked install, we want to create a new profile
mContext.startActivity(intent);
}
@Override
public void onInstallerNotAvailable() {
mHandler.post(()-> {
mAlertDialog.dismiss();
Tools.dialog(mContext,
mContext.getString(R.string.global_error),
mContext.getString(R.string.forge_dl_no_installer));
});
}
@Override
public void onDownloadError(Exception e) {
mHandler.post(mAlertDialog::dismiss);
Tools.showError(mContext, e);
}
}

View File

@ -0,0 +1,46 @@
package net.kdt.pojavlaunch.modloaders;
import android.content.Intent;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class ForgeUtils {
private static final String FORGE_METADATA_URL = "https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml";
private static final String FORGE_INSTALLER_URL = "https://maven.minecraftforge.net/net/minecraftforge/forge/%1$s/forge-%1$s-installer.jar";
public static List<String> downloadForgeVersions() throws IOException {
String forgeMetadata = DownloadUtils.downloadString(FORGE_METADATA_URL);
try {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser parser = parserFactory.newSAXParser();
ForgeVersionListHandler handler = new ForgeVersionListHandler();
parser.parse(new InputSource(new StringReader(forgeMetadata)), handler);
return handler.getVersions();
}catch (SAXException | ParserConfigurationException e) {
e.printStackTrace();
return null;
}
}
public static String getInstallerUrl(String version) {
return String.format(FORGE_INSTALLER_URL, version);
}
public static void addAutoInstallArgs(Intent intent, File modInstallerJar, boolean createProfile) {
intent.putExtra("javaArgs", "-javaagent:"+ Tools.DIR_DATA+"/forge_installer/forge_installer.jar"
+ (createProfile ? "=NPS" : "") + // No Profile Suppression
" -jar "+modInstallerJar.getAbsolutePath());
intent.putExtra("skipDetectMod", true);
}
}

View File

@ -0,0 +1,102 @@
package net.kdt.pojavlaunch.modloaders;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class ForgeVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter {
private final List<String> mGameVersions;
private final List<List<String>> mForgeVersions;
private final LayoutInflater mLayoutInflater;
public ForgeVersionListAdapter(List<String> forgeVersions, LayoutInflater layoutInflater) {
this.mLayoutInflater = layoutInflater;
mGameVersions = new ArrayList<>();
mForgeVersions = new ArrayList<>();
for(String version : forgeVersions) {
int dashIndex = version.indexOf("-");
String gameVersion = version.substring(0, dashIndex);
List<String> versionList;
int gameVersionIndex = mGameVersions.indexOf(gameVersion);
if(gameVersionIndex != -1) versionList = mForgeVersions.get(gameVersionIndex);
else {
versionList = new ArrayList<>();
mGameVersions.add(gameVersion);
mForgeVersions.add(versionList);
}
versionList.add(version);
}
}
@Override
public int getGroupCount() {
return mGameVersions.size();
}
@Override
public int getChildrenCount(int i) {
return mForgeVersions.get(i).size();
}
@Override
public Object getGroup(int i) {
return getGameVersion(i);
}
@Override
public Object getChild(int i, int i1) {
return getForgeVersion(i, i1);
}
@Override
public long getGroupId(int i) {
return i;
}
@Override
public long getChildId(int i, int i1) {
return i1;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int i, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
((TextView) convertView).setText(getGameVersion(i));
return convertView;
}
@Override
public View getChildView(int i, int i1, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
((TextView) convertView).setText(getForgeVersion(i, i1));
return convertView;
}
private String getGameVersion(int i) {
return mGameVersions.get(i);
}
private String getForgeVersion(int i, int i1){
return mForgeVersions.get(i).get(i1);
}
@Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
}

View File

@ -0,0 +1,39 @@
package net.kdt.pojavlaunch.modloaders;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
public class ForgeVersionListHandler extends DefaultHandler {
private List<String> mForgeVersions;
private StringBuilder mCurrentVersion = null;
@Override
public void startDocument() throws SAXException {
mForgeVersions = new ArrayList<>();
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if(mCurrentVersion != null) mCurrentVersion.append(ch, start, length);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(qName.equals("version")) mCurrentVersion = new StringBuilder();
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.equals("version")) {
String version = mCurrentVersion.toString();
mForgeVersions.add(version);
mCurrentVersion = null;
}
}
public List<String> getVersions() {
return mForgeVersions;
}
}

View File

@ -68,7 +68,6 @@ public class AsyncAssetManager {
Tools.copyAssetFile(ctx, "launcher_profiles.json", Tools.DIR_GAME_NEW, false); Tools.copyAssetFile(ctx, "launcher_profiles.json", Tools.DIR_GAME_NEW, false);
Tools.copyAssetFile(ctx,"resolv.conf",Tools.DIR_DATA, false); Tools.copyAssetFile(ctx,"resolv.conf",Tools.DIR_DATA, false);
Tools.copyAssetFile(ctx,"arc_dns_injector.jar",Tools.DIR_DATA, false);
} catch (IOException e) { } catch (IOException e) {
Log.e("AsyncAssetManager", "Failed to unpack critical components !"); Log.e("AsyncAssetManager", "Failed to unpack critical components !");
} }
@ -86,6 +85,8 @@ public class AsyncAssetManager {
// we repack them to a single file here // we repack them to a single file here
unpackComponent(ctx, "lwjgl3", false); unpackComponent(ctx, "lwjgl3", false);
unpackComponent(ctx, "security", true); unpackComponent(ctx, "security", true);
unpackComponent(ctx, "arc_dns_injector", true);
unpackComponent(ctx, "forge_installer", true);
} catch (IOException e) { } catch (IOException e) {
Log.e("AsyncAssetManager", "Failed o unpack components !",e ); Log.e("AsyncAssetManager", "Failed o unpack components !",e );
} }

View File

@ -352,7 +352,7 @@ public class JREUtils {
"-Dfml.earlyprogresswindow=false" //Forge 1.14+ workaround "-Dfml.earlyprogresswindow=false" //Forge 1.14+ workaround
)); ));
if(LauncherPreferences.PREF_ARC_CAPES) { if(LauncherPreferences.PREF_ARC_CAPES) {
overridableArguments.add("-javaagent:"+new File(Tools.DIR_DATA,"arc_dns_injector.jar").getAbsolutePath()+"=23.95.137.176"); overridableArguments.add("-javaagent:"+new File(Tools.DIR_DATA,"arc_dns_injector/arc_dns_injector.jar").getAbsolutePath()+"=23.95.137.176");
} }
List<String> additionalArguments = new ArrayList<>(); List<String> additionalArguments = new ArrayList<>();
for(String arg : overridableArguments) { for(String arg : overridableArguments) {

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ExpandableListView
android:id="@+id/forge_expandable_version_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</ExpandableListView>
<ProgressBar
android:id="@+id/forge_list_progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true" />
</LinearLayout>

View File

@ -372,4 +372,7 @@
<string name="preference_deadzone_scale_description">Increase it if the joystick drifts</string> <string name="preference_deadzone_scale_description">Increase it if the joystick drifts</string>
<string name="preference_force_big_core_title">Force renderer to run on the big core</string> <string name="preference_force_big_core_title">Force renderer to run on the big core</string>
<string name="preference_force_big_core_desc">Forces the Minecraft render thread to run on the core with the highest max frequency</string> <string name="preference_force_big_core_desc">Forces the Minecraft render thread to run on the core with the highest max frequency</string>
<string name="forge_dl_progress">Downloading installer for %s</string>
<string name="forge_dl_failed_to_load_list">Failed to load the version list</string>
<string name="forge_dl_no_installer">Sorry, but this version of Forge does not have an installer, which is not yet supported.</string>
</resources> </resources>

View File

@ -11,5 +11,7 @@ jar {
attributes("Manifest-Version": "1.0", attributes("Manifest-Version": "1.0",
"PreMain-Class": "git.artdeell.arcdns.ArcDNSInjectorAgent") "PreMain-Class": "git.artdeell.arcdns.ArcDNSInjectorAgent")
} }
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/")) File versionFile = file("../app_pojavlauncher/src/main/assets/components/arc_dns_injector/version")
versionFile.write(String.valueOf(new Date().getTime()))
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/components/arc_dns_injector/"))
} }

42
forge_installer/.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@ -0,0 +1,25 @@
plugins {
id 'java-library'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation 'org.json:json:20230618'
}
jar {
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
File versionFile = file("../app_pojavlauncher/src/main/assets/components/forge_installer/version")
versionFile.write(String.valueOf(new Date().getTime()))
manifest {
attributes("Manifest-Version": "1.0",
"PreMain-Class": "git.artdeell.forgeinstaller.Agent")
}
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/components/forge_installer/"))
}

View File

@ -0,0 +1,98 @@
package git.artdeell.forgeinstaller;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.WindowEvent;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
public class Agent implements AWTEventListener {
private boolean forgeWindowHandled = false;
private final boolean suppressProfileCreation;
public Agent(boolean ps) {
this.suppressProfileCreation = ps;
}
@Override
public void eventDispatched(AWTEvent event) {
WindowEvent windowEvent = (WindowEvent) event;
Window window = windowEvent.getWindow();
if(windowEvent.getID() == WindowEvent.WINDOW_OPENED) {
if(!forgeWindowHandled) { // false at startup, so we will handle the first window as the Forge one
handleForgeWindow(window);
forgeWindowHandled = true;
}else if(window instanceof JDialog) { // expecting a new dialog
handleDialog(window);
}
}
}
public void handleForgeWindow(Window window) {
List<Component> components = new ArrayList<>();
insertAllComponents(components, window, new MainWindowFilter());
AbstractButton okButton = null;
for(Component component : components) {
if(component instanceof AbstractButton) {
AbstractButton abstractButton = (AbstractButton) component;
switch(abstractButton.getText()) {
case "OK":
okButton = abstractButton; // store the button, so we can press it after processing other stuff
break;
case "Install client":
abstractButton.doClick(); // It should be the default, but let's make sure
}
}
}
if(okButton == null) {
System.out.println("Failed to set all the UI components.");
System.exit(17);
}else{
ProfileFixer.storeProfile();
EventQueue.invokeLater(okButton::doClick); // do that after forge actually builds its window, otherwise we set the path too fast
}
}
public void handleDialog(Window window) {
List<Component> components = new ArrayList<>();
insertAllComponents(components, window, new DialogFilter()); // ensure that it's a JOptionPane dialog
if(components.size() == 1) {
// another common trait of them - they only have one option pane in them,
// so we can discard the rest of the dialog structure
JOptionPane optionPane = (JOptionPane) components.get(0);
if(optionPane.getMessageType() == JOptionPane.INFORMATION_MESSAGE) { // forge doesn't emit information messages for other reasons yet
System.out.println("The install was successful!");
ProfileFixer.reinsertProfile(suppressProfileCreation);
System.exit(0); // again, forge doesn't call exit for some reason, so we do that ourselves here
}
}
}
public void insertAllComponents(List<Component> components, Container parent, ComponentFilter filter) {
int componentCount = parent.getComponentCount();
for(int i = 0; i < componentCount; i++) {
Component component = parent.getComponent(i);
if(filter.checkComponent(component)) components.add(component);
if(component instanceof Container) {
insertAllComponents(components, (Container) component, filter);
}
}
}
public static void premain(String args, Instrumentation inst) {
Toolkit.getDefaultToolkit()
.addAWTEventListener(new Agent(!"NPS".equals(args)), // No Profile Suppression
AWTEvent.WINDOW_EVENT_MASK);
}
}

View File

@ -0,0 +1,7 @@
package git.artdeell.forgeinstaller;
import java.awt.*;
public interface ComponentFilter {
boolean checkComponent(Component component);
}

View File

@ -0,0 +1,11 @@
package git.artdeell.forgeinstaller;
import javax.swing.*;
import java.awt.*;
public class DialogFilter implements ComponentFilter{
@Override
public boolean checkComponent(Component component) {
return component instanceof JOptionPane;
}
}

View File

@ -0,0 +1,13 @@
package git.artdeell.forgeinstaller;
import javax.swing.*;
import java.awt.*;
public class MainWindowFilter implements ComponentFilter{
@Override
public boolean checkComponent(Component component) {
return component instanceof JRadioButton
|| component instanceof JTextField
|| component instanceof JButton;
}
}

View File

@ -0,0 +1,59 @@
package git.artdeell.forgeinstaller;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Random;
public class ProfileFixer {
private static final Random random = new Random();
private static final Path profilesPath = Paths.get(System.getProperty("user.home"), ".minecraft", "launcher_profiles.json");
private static JSONObject oldProfile = null;
public static void storeProfile() {
try {
JSONObject minecraftProfiles = new JSONObject(
new String(Files.readAllBytes(profilesPath),
StandardCharsets.UTF_8)
);
JSONObject profilesArray = minecraftProfiles.getJSONObject("profiles");
oldProfile = profilesArray.optJSONObject("forge", null);
}catch (IOException | JSONException e) {
System.out.println("Failed to store Forge profile: "+e);
}
}
private static String pickProfileName() {
return "forge"+random.nextInt();
}
public static void reinsertProfile(boolean suppressProfileCreation) {
try {
JSONObject minecraftProfiles = new JSONObject(
new String(Files.readAllBytes(profilesPath),
StandardCharsets.UTF_8)
);
JSONObject profilesArray = minecraftProfiles.getJSONObject("profiles");
if(oldProfile != null) {
if(suppressProfileCreation) profilesArray.put("forge", oldProfile); // restore the old profile
else {
String name = pickProfileName();
while(profilesArray.has(name)) name = pickProfileName();
profilesArray.put(name, oldProfile); // restore the old profile under a new name
}
}else{
if(suppressProfileCreation) profilesArray.remove("forge"); // remove the new profile
// otherwise it wont be removed
}
minecraftProfiles.put("profiles", profilesArray);
Files.write(profilesPath, minecraftProfiles.toString().getBytes(StandardCharsets.UTF_8),
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
}catch (IOException | JSONException e) {
System.out.println("Failed to restore old Forge profile: "+e);
}
}
}

View File

@ -19,3 +19,4 @@ include ':jre_lwjgl3glfw'
include ':app_pojavlauncher' include ':app_pojavlauncher'
include ':arc_dns_injector' include ':arc_dns_injector'
include ':forge_installer'