From 1002c8ae1e06b52cd88ddc7e512939869d94d030 Mon Sep 17 00:00:00 2001 From: artdeell Date: Sat, 16 Dec 2023 15:47:13 +0300 Subject: [PATCH] Fix[cf]: unable to load CurseForge entries in the modpack menu and unable to download modpacks --- .../modloaders/modpacks/api/CommonApi.java | 10 +- .../modpacks/api/CurseforgeApi.java | 26 ++-- .../kdt/pojavlaunch/utils/DownloadUtils.java | 2 +- .../kdt/pojavlaunch/utils/GsonJsonUtils.java | 115 ++++++++++++++++++ 4 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/GsonJsonUtils.java diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java index d7aaef318..cbd43bd30 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CommonApi.java @@ -1,5 +1,7 @@ package net.kdt.pojavlaunch.modloaders.modpacks.api; +import android.util.Log; + import androidx.annotation.NonNull; import net.kdt.pojavlaunch.PojavApplication; @@ -11,7 +13,6 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; @@ -39,7 +40,6 @@ public class CommonApi implements ModpackApi { new SearchResult[mModpackApis.length] : commonApiSearchResult.searchResults; int totalSize = 0; - int totalTotalSize = 0; Future[] futures = new Future[mModpackApis.length]; for(int i = 0; i < mModpackApis.length; i++) { @@ -67,8 +67,7 @@ public class CommonApi implements ModpackApi { SearchResult searchResult = results[i] = (SearchResult) future.get(); if(searchResult != null) hasSuccessful = true; else continue; - totalSize += searchResult.results.length; - totalTotalSize += searchResult.totalResultCount; + totalSize += searchResult.totalResultCount; }catch (Exception e) { cancelAllFutures(futures); e.printStackTrace(); @@ -97,13 +96,14 @@ public class CommonApi implements ModpackApi { // Recycle or create new search result if(commonApiSearchResult == null) commonApiSearchResult = new CommonApiSearchResult(); commonApiSearchResult.searchResults = results; - commonApiSearchResult.totalResultCount = totalTotalSize; + commonApiSearchResult.totalResultCount = totalSize; commonApiSearchResult.results = concatenatedItems; return commonApiSearchResult; } @Override public ModDetail getModDetails(ModItem item) { + Log.i("CommonApi", "Invoking getModDetails on item.apiSource="+item.apiSource +" item.title="+item.title); return getModpackApi(item.apiSource).getModDetails(item); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java index 5c6d83a1e..940e4452a 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/CurseforgeApi.java @@ -20,6 +20,7 @@ import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchFilters; import net.kdt.pojavlaunch.modloaders.modpacks.models.SearchResult; import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; import net.kdt.pojavlaunch.utils.FileUtils; +import net.kdt.pojavlaunch.utils.GsonJsonUtils; import net.kdt.pojavlaunch.utils.ZipUtils; import java.io.File; @@ -125,7 +126,7 @@ public class CurseforgeApi implements ModpackApi{ break; } - hashes[i] = getSha1FromResponse(modDetail); + hashes[i] = getSha1FromModData(modDetail); } return new ModDetail(item, versionNames, mcVersionNames, versionUrls, hashes); } @@ -143,8 +144,7 @@ public class CurseforgeApi implements ModpackApi{ params.put("pageSize", CURSEFORGE_PAGINATION_SIZE); JsonObject response = mApiHandler.get("mods/"+modId+"/files", params, JsonObject.class); - if(response == null) return CURSEFORGE_PAGINATION_ERROR; - JsonArray data = response.getAsJsonArray("data"); + JsonArray data = GsonJsonUtils.getJsonArraySafe(response, "data"); if(data == null) return CURSEFORGE_PAGINATION_ERROR; for(int i = 0; i < data.size(); i++) { @@ -238,18 +238,22 @@ public class CurseforgeApi implements ModpackApi{ private @Nullable String getDownloadSha1(long projectID, long fileID) { // Try the api endpoint, die in the other case JsonObject response = mApiHandler.get("mods/"+projectID+"/files/"+fileID, JsonObject.class); - if (response == null || response.get("data").isJsonNull()) return null; - - return getSha1FromResponse(response); + JsonObject data = GsonJsonUtils.getJsonObjectSafe(response, "data"); + if(data == null) return null; + return getSha1FromModData(data); } - private String getSha1FromResponse(@NonNull JsonElement element) { - JsonArray hashes = element.getAsJsonObject().get("data").getAsJsonObject().getAsJsonArray("hashes"); + private String getSha1FromModData(@NonNull JsonObject object) { + JsonArray hashes = GsonJsonUtils.getJsonArraySafe(object, "hashes"); + if(hashes == null) return null; for (JsonElement jsonElement : hashes) { // The sha1 = 1; md5 = 2; - JsonElement algo = jsonElement.getAsJsonObject().get("algo"); - if(algo != null && algo.getAsInt() == ALGO_SHA_1){ - return jsonElement.getAsJsonObject().get("value").getAsString(); + JsonObject jsonObject = GsonJsonUtils.getJsonObjectSafe(jsonElement); + if(GsonJsonUtils.getIntSafe( + jsonObject, + "algo", + -1) == ALGO_SHA_1) { + return GsonJsonUtils.getStringSafe(jsonObject, "value"); } } return null; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/DownloadUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/DownloadUtils.java index 54acace8c..6b4c8cbec 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/DownloadUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/DownloadUtils.java @@ -200,7 +200,7 @@ public class DownloadUtils { int attempts = 0; T result = null; - while (attempts < 5 && (!outputFile.exists() || Tools.compareSHA1(outputFile, sha1))){ + while (attempts < 5 && (!outputFile.exists() || !Tools.compareSHA1(outputFile, sha1))){ attempts++; try { result = downloadFunction.call(); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/GsonJsonUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/GsonJsonUtils.java new file mode 100644 index 000000000..7472c63a9 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/GsonJsonUtils.java @@ -0,0 +1,115 @@ +package net.kdt.pojavlaunch.utils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public class GsonJsonUtils { + /** + * Safely converts a JsonElement into a JsonObject. + * @param element the input JsonElement + * @return the JsonObject if: + * the JsonElement is not null + * the JsonElement is not Json null + * the JsonElement is a JsonObjet + * null otherwise + */ + public static JsonObject getJsonObjectSafe(JsonElement element) { + if(element == null) return null; + if(element.isJsonNull() || !element.isJsonObject()) return null; + return element.getAsJsonObject(); + } + + /** + * Safely gets a JsonElement from a JsonObject + * @param jsonObject the input JsonObject + * @param memberName the member name of the JsonElement + * @return the JsonElement if: + * the input JsonObject is not null + * the input JsonObject contains an element with the specified memberName + * the JsonElement is not Json null + * null otherwise + */ + public static JsonElement getElementSafe(JsonObject jsonObject, String memberName) { + if(jsonObject == null) return null; + if(!jsonObject.has(memberName)) return null; + JsonElement element = jsonObject.get(memberName); + if(element.isJsonNull()) return null; + return element; + } + + /** + * Safely gets a JsonObject from a JsonObject + * @param jsonObject the input JsonObject + * @param memberName the member name of the output JsonObject + * @return the output JsonObject if: + * the input JsonObject is not null + * the input JsonObject contains an element with the specified memberName + * the output JsonObject is not Json null + * the output JsonObject is a JsonObjet + * null otherwise + */ + public static JsonObject getJsonObjectSafe(JsonObject jsonObject, String memberName) { + return getJsonObjectSafe(getElementSafe(jsonObject, memberName)); + } + + /** + * Safely gets a JsonArray from a JsonObject + * @param jsonObject the input JsonObject + * @param memberName the member name of the JsonArray + * @return the JsonArray if: + * the input JsonObject is not null + * the input JsonObject contains an element with the specified memberName + * the JsonArray is not Json null + * the JsonArray is a JsonArray + * null otherwise + */ + public static JsonArray getJsonArraySafe(JsonObject jsonObject, String memberName) { + JsonElement jsonElement = getElementSafe(jsonObject, memberName); + if(jsonElement == null || !jsonElement.isJsonArray()) return null; + return jsonElement.getAsJsonArray(); + } + + /** + * Safely gets an int from a JsonObject + * @param jsonObject the input JsonObject + * @param memberName the member name of the int + * @param onNullValue the value that will be returned if any of the checks fail + * @return the int if: + * the input JsonObject is not null + * the input JsonObject contains an element with the specified memberName + * the int is not Json null + * the int is an actual integer + * onNullValue otherwise + */ + public static int getIntSafe(JsonObject jsonObject, String memberName, int onNullValue) { + JsonElement jsonElement = getElementSafe(jsonObject, memberName); + if(jsonElement == null || !jsonElement.isJsonPrimitive()) return onNullValue; + try { + return jsonElement.getAsInt(); + }catch (ClassCastException e) { + return onNullValue; + } + } + + /** + * Safely gets a String from a JsonObject + * @param jsonObject the input JsonObject + * @param memberName the member name of the int + * @return the String if: + * the input JsonObject is not null + * the input JsonObject contains an element with the specified memberName + * the String is not a Json null + * the String is an actual String + * null otherwise + */ + public static String getStringSafe(JsonObject jsonObject, String memberName) { + JsonElement jsonElement = getElementSafe(jsonObject, memberName); + if(jsonElement == null || !jsonElement.isJsonPrimitive()) return null; + try { + return jsonElement.getAsString(); + }catch (ClassCastException e) { + return null; + } + } +}