mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2026-06-11 02:34:29 -04:00
[FIXME] Half-finished lwjgl3ify support
Only missing popup that installs lwjgl3ify properly if detected in mods
This commit is contained in:
@@ -36,6 +36,7 @@ import net.kdt.pojavlaunch.fragments.MicrosoftLoginFragment;
|
||||
import net.kdt.pojavlaunch.fragments.SelectAuthFragment;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextAwareDoneListener;
|
||||
import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
|
||||
import net.kdt.pojavlaunch.modloaders.LWJGL3ifyUtils;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.ModloaderInstallTracker;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.CommonApi;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModLoader;
|
||||
|
||||
@@ -280,15 +280,29 @@ public final class Tools {
|
||||
* @return Whether or not the .jar is found
|
||||
*/
|
||||
public static boolean hasMods(String... filenames) {
|
||||
return !getMods(filenames).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for mod in mods directory of current selected profile
|
||||
* Not case-sensitive
|
||||
* @param filenames Filename(s) of the .jar mod(s)
|
||||
* @return The found mods
|
||||
*/
|
||||
public static List<File> getMods(String... filenames) {
|
||||
File gameDir = getGameDir();
|
||||
File modsDir = new File(gameDir, "mods");
|
||||
File[] modFiles = modsDir.listFiles(file -> file.isFile() && file.getName().endsWith(".jar"));
|
||||
if (modFiles == null) return false;
|
||||
File[] modFiles = modsDir.listFiles(file -> file.isFile() && file.getName().toLowerCase().endsWith(".jar"));
|
||||
if (modFiles == null) return new ArrayList<>();
|
||||
List<File> foundModFiles = new ArrayList<>();
|
||||
for (File file : modFiles) {
|
||||
for (String filename : filenames)
|
||||
if (file.getName().toLowerCase().contains(filename.toLowerCase())) return true;
|
||||
if (file.getName().toLowerCase().contains(filename.toLowerCase())) {
|
||||
foundModFiles.add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return foundModFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.modloaders.LWJGL3ifyDownloadTask;
|
||||
import net.kdt.pojavlaunch.modloaders.LWJGL3ifyUtils;
|
||||
import net.kdt.pojavlaunch.modloaders.LWJGL3ifyVersionListAdapter;
|
||||
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.CommonApi;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class LWJGL3ifyInstallFragment extends ModVersionListFragment<LWJGL3ifyUtils.LWJGL3ifyVersionList>{
|
||||
public static final String TAG = "LWJGL3ifyInstallFragment";
|
||||
private ModpackApi modpackApi;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
modpackApi = new CommonApi(context.getString(R.string.curseforge_api_key));
|
||||
}
|
||||
public LWJGL3ifyInstallFragment() {
|
||||
super(TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int getTitleText() {
|
||||
return R.string.select_lwjgl3ify_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int getNoDataMsg() {
|
||||
return R.string.modloader_dl_failed_to_load_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public LWJGL3ifyUtils.LWJGL3ifyVersionList loadVersionList() throws IOException {
|
||||
return LWJGL3ifyUtils.getLWJGL3ifyVersionList(modpackApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param versionList
|
||||
* @param layoutInflater
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ExpandableListAdapter createAdapter(LWJGL3ifyUtils.LWJGL3ifyVersionList versionList, LayoutInflater layoutInflater) {
|
||||
return new LWJGL3ifyVersionListAdapter(versionList, layoutInflater);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selectedVersion
|
||||
* @param listenerProxy
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
|
||||
return new LWJGL3ifyDownloadTask(listenerProxy, (LWJGL3ifyUtils.LWJGL3ifyMod) selectedVersion, requireActivity());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param downloadedFile
|
||||
*/
|
||||
@Override
|
||||
public void onDownloadFinished(Context context, File downloadedFile) {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import static net.kdt.pojavlaunch.Tools.dialogOnUiThread;
|
||||
import static net.kdt.pojavlaunch.Tools.hasMods;
|
||||
import static net.kdt.pojavlaunch.Tools.hasNoOnlineProfileDialog;
|
||||
import static net.kdt.pojavlaunch.Tools.hasOnlineProfile;
|
||||
import static net.kdt.pojavlaunch.Tools.openPath;
|
||||
@@ -27,12 +28,14 @@ import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.modloaders.LWJGL3ifyUtils;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class MainMenuFragment extends Fragment {
|
||||
public static final String TAG = "MainMenuFragment";
|
||||
@@ -80,8 +83,13 @@ public class MainMenuFragment extends Fragment {
|
||||
.create();
|
||||
sodiumWarningDialog.show();
|
||||
} else ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true);
|
||||
|
||||
|
||||
// if (hasMods("lwjgl3ify-3")) {
|
||||
// Tools.dialogOnUiThread(requireActivity(), "Error!", "This version of LWJGL3ify is unsupported and won't work!");
|
||||
// }
|
||||
// List<File> f = Tools.getMods("lwjgl3ify-2");
|
||||
// if(!f.isEmpty()) {
|
||||
//
|
||||
// }
|
||||
});
|
||||
|
||||
mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));
|
||||
|
||||
@@ -43,6 +43,8 @@ public class ProfileTypeSelectFragment extends Fragment {
|
||||
tryInstall(NeoForgeInstallFragment.class, NeoForgeInstallFragment.TAG));
|
||||
view.findViewById(R.id.modded_profile_modpack).setOnClickListener((v)->
|
||||
tryInstall(ModpackCreateFragment.class, ModpackCreateFragment.TAG));
|
||||
view.findViewById(R.id.modded_profile_lwjgl3ify).setOnClickListener((v)->
|
||||
tryInstall(LWJGL3ifyInstallFragment.class, LWJGL3ifyInstallFragment.TAG));
|
||||
view.findViewById(R.id.modded_profile_quilt).setOnClickListener((v)->
|
||||
tryInstall(QuiltInstallFragment.class, QuiltInstallFragment.TAG));
|
||||
view.findViewById(R.id.modded_profile_bta).setOnClickListener((v)->
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64OutputStream;
|
||||
import android.util.Log;
|
||||
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
|
||||
import net.kdt.pojavlaunch.JMinecraftVersionList;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.CommonApi;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.tasks.MinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
import net.kdt.pojavlaunch.utils.FileUtils;
|
||||
import net.kdt.pojavlaunch.utils.ZipUtils;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class LWJGL3ifyDownloadTask implements Runnable, Tools.DownloaderFeedback, AsyncMinecraftDownloader.DoneListener {
|
||||
private static final String TAG = "LWJGL3ifyDownloadTask";
|
||||
private final ModloaderDownloadListener mListener;
|
||||
private final LWJGL3ifyUtils.LWJGL3ifyMod mLWJGL3ifyMod;
|
||||
private final Activity mActivity;
|
||||
|
||||
public LWJGL3ifyDownloadTask(ModloaderDownloadListener mListener, LWJGL3ifyUtils.LWJGL3ifyMod mLWJGL3ifyMod, Activity activity) {
|
||||
this.mListener = mListener;
|
||||
this.mLWJGL3ifyMod = mLWJGL3ifyMod;
|
||||
this.mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.fabric_dl_progress, "BTA");
|
||||
try {
|
||||
runCatching();
|
||||
mListener.onDownloadFinished(null);
|
||||
}catch (Exception e) {
|
||||
mListener.onDownloadError(e);
|
||||
}finally {
|
||||
ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String tryDownloadIcon() {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (Base64OutputStream base64OutputStream = new Base64OutputStream(byteArrayOutputStream, Base64.DEFAULT)){
|
||||
// Instead of appending and wasting memory with a StringBuilder, just write the prefix
|
||||
// to the stream before the base64 icon data.
|
||||
byteArrayOutputStream.write("data:image/png;base64,".getBytes(StandardCharsets.US_ASCII));
|
||||
DownloadUtils.download(mLWJGL3ifyMod.iconUrl, base64OutputStream);
|
||||
return new String(byteArrayOutputStream.toByteArray(), StandardCharsets.US_ASCII);
|
||||
}catch (IOException e) {
|
||||
Log.w(TAG, "Failed to download base64 icon", e);
|
||||
}finally {
|
||||
try {
|
||||
byteArrayOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.wtf(TAG, "Failed to close a byte array stream??", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File tryDownloadModJar() throws IOException {
|
||||
try {
|
||||
File jarFile = new File(Tools.DIR_CACHE, "lwjgl3ify-jars/lwjgl3ify-"+ mLWJGL3ifyMod.versionName+".jar");
|
||||
if (!(jarFile.exists() && Objects.equals(getSha1(jarFile), mLWJGL3ifyMod.hash)))
|
||||
DownloadUtils.downloadFileMonitored(
|
||||
mLWJGL3ifyMod.downloadUrl,
|
||||
jarFile,
|
||||
new byte[8192],
|
||||
this
|
||||
);
|
||||
return jarFile;
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
throw new IOException("Unable to download LWJGL3ify from " + mLWJGL3ifyMod.downloadUrl, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryDownloadDeps(File modsDir) throws IOException {
|
||||
List<LWJGL3ifyUtils.LWJGL3ifyMod> deps = LWJGL3ifyUtils.collectDependencies(mLWJGL3ifyMod, new CommonApi(mActivity.getString(R.string.curseforge_api_key)));
|
||||
for (int i=0; i < deps.size(); ++i) {
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = new URI(deps.get(i).downloadUrl);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String path = uri.getPath();
|
||||
String fileName = new File(path).getName();
|
||||
try {
|
||||
DownloadUtils.downloadFileMonitored(
|
||||
deps.get(i).downloadUrl,
|
||||
new File(modsDir, fileName),
|
||||
new byte[8192],
|
||||
this
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Unable to download"+deps.get(i).versionName+" from " + deps.get(i).downloadUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getSha1(File file) throws IOException, NoSuchAlgorithmException {
|
||||
MessageDigest algorithm = MessageDigest.getInstance("SHA-1");
|
||||
//noinspection IOStreamConstructor It will reccomend you use an API26 function like a dumb
|
||||
DigestInputStream hashingStream = new DigestInputStream(new FileInputStream(file), algorithm);
|
||||
byte[] buffer = new byte[8192];
|
||||
while (hashingStream.read(buffer) != -1) {} // just read to update the digest
|
||||
hashingStream.close();
|
||||
byte[] digest = algorithm.digest();
|
||||
StringBuilder sb = new StringBuilder(digest.length * 2);
|
||||
for (byte b : digest) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static JMinecraftVersionList.Version installJson(String profileID, File modJar) throws IOException {
|
||||
try {
|
||||
JMinecraftVersionList.Version version = Tools.GLOBAL_GSON.fromJson(
|
||||
Tools.read(
|
||||
ZipUtils.getEntryStream(
|
||||
new ZipFile(modJar),
|
||||
"me/eigenraven/lwjgl3ify/relauncher/version.json"
|
||||
)
|
||||
),
|
||||
JMinecraftVersionList.Version.class);
|
||||
version.id = profileID;
|
||||
Tools.write(Tools.DIR_HOME_VERSION + "/" + profileID + "/" + profileID + ".json", Tools.GLOBAL_GSON.toJson(version));
|
||||
return version;
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to install "+profileID+" json file.");
|
||||
}
|
||||
}
|
||||
|
||||
private MinecraftProfile createProfile(String profileID) {
|
||||
MinecraftProfile LWJGL3ifyProfile = new MinecraftProfile();
|
||||
LWJGL3ifyProfile.lastVersionId = profileID;
|
||||
LWJGL3ifyProfile.name = "LWJGL3ify - "+ mLWJGL3ifyMod.versionName;
|
||||
// Allows for smooth-ish upgrades. LWJGL3ify thankfully follows semver.
|
||||
LWJGL3ifyProfile.gameDir = String.format("./custom_instances/LWJGL3ify_%s", mLWJGL3ifyMod.versionName);
|
||||
LWJGL3ifyProfile.icon = tryDownloadIcon();
|
||||
return LWJGL3ifyProfile;
|
||||
}
|
||||
|
||||
private void createInstance(MinecraftProfile profile, File modJar) throws IOException {
|
||||
File modsDir = new File(Tools.DIR_GAME_HOME, profile.gameDir+"/mods");
|
||||
if (modsDir.isFile()) {
|
||||
if (!modsDir.delete()) {
|
||||
throw new IOException("Failed to delete file where directory should be: " + modsDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
try {
|
||||
FileUtils.ensureDirectory(modsDir);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create folder " + modsDir.getAbsolutePath());
|
||||
}
|
||||
// Copy downloaded cached mod jar
|
||||
try (FileInputStream fis = new FileInputStream(modJar);
|
||||
FileOutputStream fos = new FileOutputStream(new File(modsDir, "lwjgl3ify-"+ mLWJGL3ifyMod.versionName+".jar"))) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = fis.read(buffer)) > 0) {
|
||||
fos.write(buffer, 0, length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
tryDownloadDeps(modsDir);
|
||||
}
|
||||
|
||||
public void runCatching() throws IOException {
|
||||
// This cannot be allowed to match the mod.jar ID otherwise conflicts occur and GLFW input breaks
|
||||
String LWJGL3ifyProfileID = "1.7.10-LWJGL3ify-"+ mLWJGL3ifyMod.versionName;
|
||||
File modJar = tryDownloadModJar();
|
||||
if (!modJar.exists()) throw new IOException("Failed to download LWJGL3ify "+ mLWJGL3ifyMod.versionName);
|
||||
MinecraftProfile profile = createProfile(LWJGL3ifyProfileID);
|
||||
new MinecraftDownloader().start(mActivity, installJson(LWJGL3ifyProfileID, modJar), LWJGL3ifyProfileID, this);
|
||||
createInstance(profile, modJar);
|
||||
|
||||
|
||||
LauncherProfiles.load();
|
||||
LauncherProfiles.insertMinecraftProfile(profile);
|
||||
LauncherProfiles.write();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(int curr, int max) {
|
||||
int progress100 = (int)(((float)curr / (float)max)*100f);
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.of_dl_progress, mLWJGL3ifyMod.versionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFailed(Throwable throwable) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.api.ModpackApi;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.models.Constants;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModDetail;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.models.ModItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LWJGL3ifyUtils {
|
||||
public static LWJGL3ifyVersionList getLWJGL3ifyVersionList(ModpackApi modpackApi) throws IOException {
|
||||
ModDetail lwjgl3ifyModDetail = getLWJGL3ifyModDetail(modpackApi);
|
||||
List<LWJGL3ifyMod> supportedVersions = new ArrayList<>(), brokenVersions = new ArrayList<>();
|
||||
for (int i = 0; i < lwjgl3ifyModDetail.versionNames.length; i++) {
|
||||
String normalizedVersion = normalizeVersionName(lwjgl3ifyModDetail.versionNames[i], lwjgl3ifyModDetail.apiSource);
|
||||
boolean isSupportedVersion = Integer.parseInt(normalizedVersion.split("\\.")[0]) < 3;
|
||||
|
||||
LWJGL3ifyMod version = new LWJGL3ifyMod(
|
||||
normalizedVersion,
|
||||
lwjgl3ifyModDetail.versionUrls[i],
|
||||
lwjgl3ifyModDetail.imageUrl,
|
||||
lwjgl3ifyModDetail.id,
|
||||
lwjgl3ifyModDetail.versionHashes[i],
|
||||
lwjgl3ifyModDetail.dependencies[i]
|
||||
);
|
||||
// LWJGL3ify 3.x uses SDL which needs to be fixed first
|
||||
(isSupportedVersion ? supportedVersions : brokenVersions).add(version);
|
||||
}
|
||||
return new LWJGL3ifyVersionList(supportedVersions, brokenVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jarName LWJGL3ify jar name, the same as Curseforge {@code versionNames} (ex. {@code 3.0.16}, {@code 2.1.18})
|
||||
* @param source Either {@link Constants#SOURCE_MODRINTH} or {@link Constants#SOURCE_CURSEFORGE}
|
||||
* @return Filled out {@link LWJGL3ifyMod} corresponding to version provided
|
||||
* @throws IllegalArgumentException If LWJGL3ify version was not found in the source provided
|
||||
* @throws IOException If provided source is not what was expected
|
||||
*/
|
||||
public static LWJGL3ifyMod getLWJGL3ifyVersion(String jarName, ModpackApi source) throws IOException {
|
||||
// This is a hack but it should work
|
||||
String providedNormalizedVersion = normalizeVersionName(jarName, Constants.SOURCE_CURSEFORGE);
|
||||
ModDetail lwjgl3ifyModDetail = getLWJGL3ifyModDetail(source);
|
||||
for (int i = 0; i < lwjgl3ifyModDetail.versionNames.length; i++) {
|
||||
String normalizedVersion = normalizeVersionName(lwjgl3ifyModDetail.versionNames[i], lwjgl3ifyModDetail.apiSource);
|
||||
if (providedNormalizedVersion.equals(normalizedVersion))
|
||||
return new LWJGL3ifyMod(
|
||||
providedNormalizedVersion,
|
||||
lwjgl3ifyModDetail.versionUrls[i],
|
||||
lwjgl3ifyModDetail.imageUrl,
|
||||
lwjgl3ifyModDetail.id,
|
||||
lwjgl3ifyModDetail.versionHashes[i],
|
||||
lwjgl3ifyModDetail.dependencies[i]
|
||||
);
|
||||
}
|
||||
String sourceName = (lwjgl3ifyModDetail.apiSource == Constants.SOURCE_MODRINTH) ? "Modrinth" : "Curseforge";
|
||||
throw new IllegalArgumentException("Cannot find LWJGL3ify version "+providedNormalizedVersion+" from "+sourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Flat list of all dependencies needed by {@code lwjgl3ifyMod}.
|
||||
*/
|
||||
public static List<LWJGL3ifyMod> collectDependencies(LWJGL3ifyMod lwjgl3ifyMod, ModpackApi modpackApi) {
|
||||
List<LWJGL3ifyMod> allDeps = new ArrayList<>();
|
||||
for (ModDetail.Dependencies dep : lwjgl3ifyMod.dependencies) {
|
||||
ModDetail detail = getModDetail(modpackApi, dep.project_id);
|
||||
if (detail != null) {
|
||||
LWJGL3ifyMod newMod = new LWJGL3ifyMod(
|
||||
detail.versionNames[0],
|
||||
detail.versionUrls[0],
|
||||
detail.imageUrl,
|
||||
detail.id,
|
||||
detail.versionHashes[0],
|
||||
detail.dependencies[0]
|
||||
);
|
||||
|
||||
allDeps.add(newMod);
|
||||
// omg recursion!?!?!
|
||||
allDeps.addAll(collectDependencies(newMod, modpackApi));
|
||||
}
|
||||
}
|
||||
return allDeps;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String normalizeVersionName(String versionName, int apiSource) {
|
||||
if (apiSource == Constants.SOURCE_MODRINTH) { // Ex. 3.0.16 - 1.7.10
|
||||
versionName = versionName.replaceAll(" - .*", "");
|
||||
}else if (apiSource == Constants.SOURCE_CURSEFORGE) { // Ex. lwjgl3ify-3.0.16.jar - 1.7.10
|
||||
versionName = versionName.split("-")[1].replace(".jar", "");
|
||||
}else throw new IllegalArgumentException("LWJGL3ify is only available on Modrinth or Curseforge!");
|
||||
return versionName;
|
||||
}
|
||||
|
||||
private static ModDetail getModDetail(ModpackApi modpackApi, String id){
|
||||
ModDetail modDetail = null;
|
||||
// Modrinth is more complete in this context. Curseforge is missing some releases.
|
||||
if (modDetail == null && id != null && !id.isEmpty()) {
|
||||
modDetail = fetch(modpackApi, Constants.SOURCE_MODRINTH, id);
|
||||
}
|
||||
try {
|
||||
if (modDetail == null && id != null && !id.isEmpty()) {
|
||||
Integer.parseInt(id); // Triggers exception, skipping call to CF if provided id isn't int
|
||||
modDetail = fetch(modpackApi, Constants.SOURCE_CURSEFORGE, id);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
return modDetail;
|
||||
}
|
||||
|
||||
private static ModDetail fetch(ModpackApi modpackApi, int source, String id) {
|
||||
ModItem item = new ModItem(source, false, id, null, null, null);
|
||||
return modpackApi.getModDetails(item);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static ModDetail getLWJGL3ifyModDetail(ModpackApi modpackApi) throws IOException {
|
||||
ModDetail lwjgl3ifyModDetail = getModDetail(modpackApi, "lwjgl3ify");
|
||||
if (lwjgl3ifyModDetail == null) // Hardcoded ID is a bad idea, but it'll work well enough
|
||||
lwjgl3ifyModDetail = getModDetail(modpackApi, "998880");
|
||||
if (lwjgl3ifyModDetail == null) throw new IOException("Unable to fetch LWJGL3ify version list from Curseforge and Modrinth. " +
|
||||
"Please check your internet connection and whether Modrinth and Curseforge are accessible.");
|
||||
return lwjgl3ifyModDetail;
|
||||
}
|
||||
// TODO: Turn this into a generic ModItem class for general mods and refactor that crusty modpack naming scheme
|
||||
public static class LWJGL3ifyMod {
|
||||
public final String versionName;
|
||||
public final String downloadUrl;
|
||||
public final String iconUrl;
|
||||
public final String id;
|
||||
public final String hash;
|
||||
public final ModDetail.Dependencies[] dependencies;
|
||||
|
||||
public LWJGL3ifyMod(String versionName, String downloadUrl, String iconUrl, String id, String hash, ModDetail.Dependencies[] dependencies) {
|
||||
this.versionName = versionName;
|
||||
this.downloadUrl = downloadUrl;
|
||||
this.iconUrl = iconUrl;
|
||||
this.id = id;
|
||||
this.hash = hash;
|
||||
this.dependencies = dependencies;
|
||||
}
|
||||
}
|
||||
public static class LWJGL3ifyVersionList {
|
||||
public final List<LWJGL3ifyMod> supportedVersions;
|
||||
public final List<LWJGL3ifyMod> brokenVersions; // SDL versions for now
|
||||
|
||||
public LWJGL3ifyVersionList(List<LWJGL3ifyMod> mSupportedVersions, List<LWJGL3ifyMod> mBrokenVersions) {
|
||||
this.supportedVersions = mSupportedVersions;
|
||||
this.brokenVersions = mBrokenVersions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.content.Context;
|
||||
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 net.kdt.pojavlaunch.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LWJGL3ifyVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter {
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final ArrayList<String> mGroupNames;
|
||||
private final ArrayList<List<LWJGL3ifyUtils.LWJGL3ifyMod>> mGroups;
|
||||
|
||||
public LWJGL3ifyVersionListAdapter(LWJGL3ifyUtils.LWJGL3ifyVersionList versionList, LayoutInflater mLayoutInflater) {
|
||||
this.mLayoutInflater = mLayoutInflater;
|
||||
Context context = mLayoutInflater.getContext();
|
||||
mGroupNames = new ArrayList<>(2);
|
||||
mGroups = new ArrayList<>(2);
|
||||
if(!versionList.supportedVersions.isEmpty()) {
|
||||
mGroupNames.add(context.getString(R.string.lwjgl3ify_installer_available_versions));
|
||||
mGroups.add(versionList.supportedVersions);
|
||||
}
|
||||
if(!versionList.brokenVersions.isEmpty()) {
|
||||
mGroupNames.add(context.getString(R.string.lwjgl3ify_installer_broken_versions));
|
||||
mGroups.add(versionList.brokenVersions);
|
||||
}
|
||||
mGroupNames.trimToSize();
|
||||
mGroups.trimToSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return mGroups.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int i) {
|
||||
return mGroups.get(i).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroup(int i) {
|
||||
return mGroupNames.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LWJGL3ifyUtils.LWJGL3ifyMod getChild(int i, int i1) {
|
||||
return mGroups.get(i).get(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((String)getGroup(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(getChild(i,i1).versionName);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int i, int i1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -100,6 +100,7 @@ public class CurseforgeApi implements ModpackApi{
|
||||
|
||||
@Override
|
||||
public ModDetail getModDetails(ModItem item) {
|
||||
fillInMissingModItemData(item);
|
||||
ArrayList<JsonObject> allModDetails = new ArrayList<>();
|
||||
int index = 0;
|
||||
while(index != CURSEFORGE_PAGINATION_END_REACHED &&
|
||||
@@ -109,17 +110,32 @@ public class CurseforgeApi implements ModpackApi{
|
||||
if(index == CURSEFORGE_PAGINATION_ERROR) return null;
|
||||
int length = allModDetails.size();
|
||||
String[] versionNames = new String[length];
|
||||
String[] versionIds = new String[length];
|
||||
String[] mcVersionNames = new String[length];
|
||||
String[] versionUrls = new String[length];
|
||||
String[] hashes = new String[length];
|
||||
ModDetail.Dependencies[][] dependencies = new ModDetail.Dependencies[length][];
|
||||
for(int i = 0; i < allModDetails.size(); i++) {
|
||||
JsonObject modDetail = allModDetails.get(i);
|
||||
versionNames[i] = modDetail.get("displayName").getAsString();
|
||||
|
||||
versionIds[i] = modDetail.get("id").getAsString();
|
||||
JsonElement downloadUrl = modDetail.get("downloadUrl");
|
||||
versionUrls[i] = downloadUrl.getAsString();
|
||||
|
||||
JsonArray gameVersions = modDetail.getAsJsonArray("gameVersions");
|
||||
try {
|
||||
JsonArray dependenciesJsonArray = modDetail.getAsJsonArray("dependencies");
|
||||
dependencies[i] = new ModDetail.Dependencies[dependenciesJsonArray.size()];
|
||||
for (int i1 = 0; i1 < dependenciesJsonArray.size(); ++i1) {
|
||||
JsonObject obj = dependenciesJsonArray.get(i1).getAsJsonObject();
|
||||
dependencies[i][i1] = new ModDetail.Dependencies(
|
||||
GsonJsonUtils.getStringSafe(obj, "modId"),
|
||||
null,
|
||||
null, // These two are only present on modrinth
|
||||
GsonJsonUtils.getStringSafe(obj, "relationType")
|
||||
);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
for(JsonElement jsonElement : gameVersions) {
|
||||
String gameVersion = jsonElement.getAsString();
|
||||
if(!sMcVersionPattern.matcher(gameVersion).matches()) {
|
||||
@@ -131,7 +147,26 @@ public class CurseforgeApi implements ModpackApi{
|
||||
|
||||
hashes[i] = getSha1FromModData(modDetail);
|
||||
}
|
||||
return new ModDetail(item, versionNames, mcVersionNames, versionUrls, hashes);
|
||||
return new ModDetail(item, versionNames, versionIds, mcVersionNames, versionUrls, hashes, dependencies);
|
||||
}
|
||||
|
||||
private void fillInMissingModItemData(ModItem item) {
|
||||
if (!(item.title == null || item.description == null || item.imageUrl == null)) return;
|
||||
JsonObject response = mApiHandler.get(String.format("mods/%s", item.id), JsonObject.class);
|
||||
JsonObject data = GsonJsonUtils.getJsonObjectSafe(response, "data");
|
||||
if (data == null) return;
|
||||
if (item.title == null) {
|
||||
JsonElement title = data.get("name");
|
||||
item.title = title != null ? title.getAsString() : "";
|
||||
}
|
||||
if (item.description == null) {
|
||||
JsonElement description = data.get("summary");
|
||||
item.description = description != null ? description.getAsString() : "";
|
||||
}
|
||||
if (item.imageUrl == null) {
|
||||
JsonElement imageUrl = data.getAsJsonObject("logo").get("thumbnailUrl");
|
||||
item.imageUrl = imageUrl != null ? imageUrl.getAsString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
|
||||
@@ -16,12 +17,15 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.ModrinthIndex;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters;
|
||||
import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult;
|
||||
import net.kdt.pojavlaunch.progresskeeper.DownloaderProgressWrapper;
|
||||
import net.kdt.pojavlaunch.utils.GsonJsonUtils;
|
||||
import net.kdt.pojavlaunch.utils.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -86,18 +90,35 @@ public class ModrinthApi implements ModpackApi{
|
||||
|
||||
@Override
|
||||
public ModDetail getModDetails(ModItem item) {
|
||||
|
||||
fillInMissingModItemData(item);
|
||||
JsonArray response = mApiHandler.get(String.format("project/%s/version", item.id), JsonArray.class);
|
||||
if(response == null) return null;
|
||||
System.out.println(response);
|
||||
String[] names = new String[response.size()];
|
||||
String[] ids = new String[response.size()];
|
||||
String[] mcNames = new String[response.size()];
|
||||
String[] urls = new String[response.size()];
|
||||
String[] hashes = new String[response.size()];
|
||||
ModDetail.Dependencies[][] dependencies = new ModDetail.Dependencies[response.size()][];
|
||||
|
||||
for (int i=0; i<response.size(); ++i) {
|
||||
JsonObject version = response.get(i).getAsJsonObject();
|
||||
names[i] = version.get("name").getAsString();
|
||||
ids[i] = version.get("id").getAsString();
|
||||
try {
|
||||
JsonArray dependenciesJsonArray = version.getAsJsonArray("dependencies");
|
||||
dependencies[i] = new ModDetail.Dependencies[dependenciesJsonArray.size()];
|
||||
for (int i1 = 0; i1 < dependenciesJsonArray.size(); ++i1) {
|
||||
JsonObject obj = dependenciesJsonArray.get(i1).getAsJsonObject();
|
||||
dependencies[i][i1] = new ModDetail.Dependencies(
|
||||
GsonJsonUtils.getStringSafe(obj, "project_id"),
|
||||
GsonJsonUtils.getStringSafe(obj, "version_id"),
|
||||
GsonJsonUtils.getStringSafe(obj, "file_name"),
|
||||
GsonJsonUtils.getStringSafe(obj, "dependency_type")
|
||||
);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
mcNames[i] = version.get("game_versions").getAsJsonArray().get(0).getAsString();
|
||||
urls[i] = version.get("files").getAsJsonArray().get(0).getAsJsonObject().get("url").getAsString();
|
||||
// Assume there may not be hashes, in case the API changes
|
||||
@@ -111,7 +132,25 @@ public class ModrinthApi implements ModpackApi{
|
||||
hashes[i] = hashesMap.get("sha1").getAsString();
|
||||
}
|
||||
|
||||
return new ModDetail(item, names, mcNames, urls, hashes);
|
||||
return new ModDetail(item, names, ids, mcNames, urls, hashes, dependencies);
|
||||
}
|
||||
|
||||
private void fillInMissingModItemData(ModItem item) {
|
||||
if (!(item.title == null || item.description == null || item.imageUrl == null)) return;
|
||||
JsonObject projectResponse = mApiHandler.get(String.format("project/%s", item.id), JsonObject.class);
|
||||
if (projectResponse == null) return;
|
||||
if (item.title == null) {
|
||||
JsonElement title = projectResponse.get("title");
|
||||
item.title = title != null ? title.getAsString() : "";
|
||||
}
|
||||
if (item.description == null) {
|
||||
JsonElement description = projectResponse.get("description");
|
||||
item.description = description != null ? description.getAsString() : "";
|
||||
}
|
||||
if (item.imageUrl == null) {
|
||||
JsonElement imageUrl = projectResponse.get("icon_url");
|
||||
item.imageUrl = imageUrl != null ? imageUrl.getAsString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,12 +12,16 @@ public class ModDetail extends ModItem {
|
||||
public String[] versionUrls;
|
||||
/* SHA 1 hashes, null if a hash is unavailable */
|
||||
public String[] versionHashes;
|
||||
public ModDetail(ModItem item, String[] versionNames, String[] mcVersionNames, String[] versionUrls, String[] hashes) {
|
||||
public String[] versionIds;
|
||||
public Dependencies[][] dependencies;
|
||||
public ModDetail(ModItem item, String[] versionNames, String[] versionIds, String[] mcVersionNames, String[] versionUrls, String[] hashes, Dependencies[][] dependencies) {
|
||||
super(item.apiSource, item.isModpack, item.id, item.title, item.description, item.imageUrl);
|
||||
this.versionNames = versionNames;
|
||||
this.mcVersionNames = mcVersionNames;
|
||||
this.versionIds = versionIds;
|
||||
this.versionUrls = versionUrls;
|
||||
this.versionHashes = hashes;
|
||||
this.dependencies = dependencies;
|
||||
|
||||
// Add the mc version to the version model
|
||||
for (int i=0; i<versionNames.length; i++){
|
||||
@@ -32,7 +36,8 @@ public class ModDetail extends ModItem {
|
||||
return "ModDetail{" +
|
||||
"versionNames=" + Arrays.toString(versionNames) +
|
||||
", mcVersionNames=" + Arrays.toString(mcVersionNames) +
|
||||
", versionIds=" + Arrays.toString(versionUrls) +
|
||||
", versionIds=" + Arrays.toString(versionIds) +
|
||||
", versionUrls=" + Arrays.toString(versionUrls) +
|
||||
", id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
@@ -41,4 +46,17 @@ public class ModDetail extends ModItem {
|
||||
", isModpack=" + isModpack +
|
||||
'}';
|
||||
}
|
||||
public static class Dependencies{
|
||||
public String project_id; // the main id in item.id
|
||||
public String version_id;
|
||||
public String file_name;
|
||||
public String dependency_type;
|
||||
public Dependencies(String project_id, String version_id, String file_name, String dependency_type){
|
||||
this.project_id = project_id;
|
||||
this.version_id = version_id;
|
||||
this.file_name = file_name;
|
||||
this.dependency_type = dependency_type;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,6 +119,14 @@
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
android:text="@string/modpack_install_button" />
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/modded_profile_lwjgl3ify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/padding_large"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
android:text="@string/create_lwjgl3ify_profile" />
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/modded_profile_bta"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -508,5 +508,9 @@
|
||||
<string name="global_download">Download</string>
|
||||
<string name="multirt_no_internet">Failed to download, check your internet connection.</string>
|
||||
<string name="global_installing">Installing…</string>
|
||||
<string name="create_lwjgl3ify_profile">Create LWJGL3ify profile</string>
|
||||
<string name="select_lwjgl3ify_version">Select LWJGL3ify version</string>
|
||||
<string name="lwjgl3ify_installer_available_versions">Supported LWJGL3IFY versions</string>
|
||||
<string name="lwjgl3ify_installer_broken_versions">Broken LWJGL3ify versions (SDL)</string>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user