diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 778732a7f..180896b68 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -85,9 +85,9 @@ public final class Tools { public static final Gson GLOBAL_GSON = new GsonBuilder().setPrettyPrinting().create(); public static final String URL_HOME = "https://pojavlauncherteam.github.io"; - public static String NATIVE_LIB_DIR; public static String DIR_DATA; //Initialized later to get context + public static File DIR_CACHE; public static String MULTIRT_HOME; public static String LOCAL_RENDERER = null; public static int DEVICE_ARCHITECTURE; @@ -139,6 +139,7 @@ public final class Tools { * Any value (in)directly dependant on DIR_DATA should be set only here. */ public static void initContextConstants(Context ctx){ + DIR_CACHE = ctx.getCacheDir(); DIR_DATA = ctx.getFilesDir().getParent(); MULTIRT_HOME = DIR_DATA+"/runtimes"; DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath(); @@ -897,7 +898,7 @@ public final class Tools { sExecutorService.execute(() -> { try { final String name = getFileName(activity, uri); - final File modInstallerFile = new File(activity.getCacheDir(), name); + final File modInstallerFile = new File(Tools.DIR_CACHE, name); FileOutputStream fos = new FileOutputStream(modInstallerFile); InputStream input = activity.getContentResolver().openInputStream(uri); IOUtils.copy(input, fos); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/FabricInstallFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/FabricInstallFragment.java index 8980f1061..d6659d678 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/FabricInstallFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/FabricInstallFragment.java @@ -48,7 +48,7 @@ public class FabricInstallFragment extends Fragment implements AdapterView.OnIte @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - this.mDestinationDir = new File(context.getCacheDir(), "fabric-installer"); + this.mDestinationDir = new File(Tools.DIR_CACHE, "fabric-installer"); } @Override diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ForgeInstallFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ForgeInstallFragment.java index 81e56b5aa..1cc86e620 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ForgeInstallFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ForgeInstallFragment.java @@ -42,7 +42,7 @@ public class ForgeInstallFragment extends Fragment implements Runnable, View.OnC public void onAttach(@NonNull Context context) { super.onAttach(context); this.mInflater = LayoutInflater.from(context); - this.mDestinationFile = new File(context.getCacheDir(), "forge-installer.jar"); + this.mDestinationFile = new File(Tools.DIR_CACHE, "forge-installer.jar"); } @Override diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/FabricUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/FabricUtils.java index bc4a60d6c..83f849a78 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/FabricUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/FabricUtils.java @@ -18,21 +18,46 @@ public class FabricUtils { private static final String FABRIC_INSTALLER_METADATA_URL = "https://meta.fabricmc.net/v2/versions/installer"; private static final String FABRIC_LOADER_METADATA_URL = "https://meta.fabricmc.net/v2/versions/loader"; public static List downloadLoaderVersionList(boolean onlyStable) throws IOException { - String loaderMetadata = DownloadUtils.downloadString(FABRIC_LOADER_METADATA_URL); - List loaderList = new ArrayList<>(); - if(enumerateMetadata(loaderMetadata, (object)->{ - if(onlyStable && !object.getBoolean("stable")) return false; - loaderList.add(object.getString("version")); - return false; - }) == null) return null; - return loaderList; + try { + return DownloadUtils.downloadStringCached(FABRIC_LOADER_METADATA_URL, + "fabric_loader_versions", (input)->{ + final List loaderList = new ArrayList<>(); + try { + enumerateMetadata(input, (object) -> { + if (onlyStable && !object.getBoolean("stable")) return false; + loaderList.add(object.getString("version")); + return false; + }); + }catch (JSONException e) { + throw new DownloadUtils.ParseException(e); + } + return loaderList; + }); + }catch (DownloadUtils.ParseException e) { + e.printStackTrace(); + return null; + } } public static String[] getInstallerUrlAndVersion() throws IOException{ String installerMetadata = DownloadUtils.downloadString(FABRIC_INSTALLER_METADATA_URL); - JSONObject selectedMetadata = enumerateMetadata(installerMetadata, (object)-> object.getBoolean("stable")); - if(selectedMetadata == null) return null; - return new String[] {selectedMetadata.optString("url"), selectedMetadata.optString("version")}; + try { + return DownloadUtils.downloadStringCached(FABRIC_INSTALLER_METADATA_URL, + "fabric_installer_versions", input -> { + try { + JSONObject selectedMetadata = enumerateMetadata(installerMetadata, (object) -> + object.getBoolean("stable")); + if (selectedMetadata == null) return null; + return new String[]{selectedMetadata.getString("url"), + selectedMetadata.getString("version")}; + } catch (JSONException e) { + throw new DownloadUtils.ParseException(e); + } + }); + }catch (DownloadUtils.ParseException e) { + e.printStackTrace(); + return null; + } } public static void addAutoInstallArgs(Intent intent, File modInstalllerJar, @@ -46,8 +71,7 @@ public class FabricUtils { } - private static JSONObject enumerateMetadata(String inputMetadata, FabricMetaReader metaReader) { - try { + private static JSONObject enumerateMetadata(String inputMetadata, FabricMetaReader metaReader) throws JSONException{ JSONArray fullMetadata = new JSONArray(inputMetadata); JSONObject metadataObject = null; for(int i = 0; i < fullMetadata.length(); i++) { @@ -55,9 +79,5 @@ public class FabricUtils { if(metaReader.processMetadata(metadataObject)) return metadataObject; } return metadataObject; - }catch (JSONException e) { - e.printStackTrace(); - return null; - } } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/ForgeUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/ForgeUtils.java index 588b90430..7c68ccad1 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/ForgeUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/ForgeUtils.java @@ -21,17 +21,32 @@ 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 downloadForgeVersions() throws IOException { - String forgeMetadata = DownloadUtils.downloadString(FORGE_METADATA_URL); + SAXParser saxParser; try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - SAXParser parser = parserFactory.newSAXParser(); - ForgeVersionListHandler handler = new ForgeVersionListHandler(); - parser.parse(new InputSource(new StringReader(forgeMetadata)), handler); - return handler.getVersions(); + saxParser = parserFactory.newSAXParser(); }catch (SAXException | ParserConfigurationException e) { + e.printStackTrace(); + // if we cant make a parser we might as well not even try to parse anything + return null; + } + try { + return DownloadUtils.downloadStringCached(FORGE_METADATA_URL, "forge_versions", input -> { + try { + ForgeVersionListHandler handler = new ForgeVersionListHandler(); + saxParser.parse(new InputSource(new StringReader(input)), handler); + return handler.getVersions(); + // IOException is present here StringReader throws it only if the parser called close() + // sooner than needed, which is a parser issue and not an I/O one + }catch (SAXException | IOException e) { + throw new DownloadUtils.ParseException(e); + } + }); + }catch (DownloadUtils.ParseException e) { e.printStackTrace(); return null; } + } public static String getInstallerUrl(String version) { return String.format(FORGE_INSTALLER_URL, version); 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 ed4767fca..73c702ace 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 @@ -1,5 +1,7 @@ package net.kdt.pojavlaunch.utils; +import android.util.Log; + import androidx.annotation.Nullable; import java.io.*; @@ -115,6 +117,46 @@ public class DownloadUtils { conn.disconnect(); } + public static T downloadStringCached(String url, String cacheName, ParseCallback parseCallback) throws IOException, ParseException{ + File cacheDestination = new File(Tools.DIR_CACHE, "string_cache/"+cacheName); + File cacheDestinationDir = cacheDestination.getParentFile(); + if(cacheDestinationDir != null && + !cacheDestinationDir.exists() && + !cacheDestinationDir.mkdirs()) throw new IOException("Failed to create the cache directory"); + if(cacheDestination.isFile() && + cacheDestination.canRead() && + System.currentTimeMillis() < (cacheDestination.lastModified() + 86400000)) { + try { + String cachedString = Tools.read(new FileInputStream(cacheDestination)); + return parseCallback.process(cachedString); + }catch(IOException e) { + Log.i("DownloadUtils", "Failed to read the cached file", e); + }catch (ParseException e) { + Log.i("DownloadUtils", "Failed to parse the cached file", e); + } + } + String urlContent = DownloadUtils.downloadString(url); + // if we download the file and fail parsing it, we will yeet outta there + // and not cache the unparseable sting. We will return this after trying to save the downloaded + // string into cache + T parseResult = parseCallback.process(urlContent); + + boolean tryWriteCache = false; + if(cacheDestination.exists()) { + tryWriteCache = cacheDestination.canWrite(); + } else { + // if it is null, then cacheDestination is the file system root. Very bad. + // but let's shield ourselves and just never try to cache the file if that happens + if(cacheDestinationDir != null && !cacheDestinationDir.isFile()) tryWriteCache = cacheDestinationDir.canWrite(); + } + if(tryWriteCache) try { + Tools.write(cacheDestination.getAbsolutePath(), urlContent); + }catch(IOException e) { + Log.i("DownloadUtils", "Failed to cache the string", e); + } + return parseResult; + } + public static void downloadFileMonitoredWithHeaders(String urlInput,File outputFile, @Nullable byte[] buffer, Tools.DownloaderFeedback monitor, String userAgent, String cookies) throws IOException { if (!outputFile.exists()) { @@ -141,5 +183,13 @@ public class DownloadUtils { conn.disconnect(); } + public interface ParseCallback { + T process(String input) throws ParseException; + } + public static class ParseException extends Exception { + public ParseException(Exception e) { + super(e); + } + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index 542d724ac..b2e98cf4f 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -173,7 +173,7 @@ public class JREUtils { envMap.put("POJAV_NATIVEDIR", NATIVE_LIB_DIR); envMap.put("JAVA_HOME", jreHome); envMap.put("HOME", Tools.DIR_GAME_HOME); - envMap.put("TMPDIR", activity.getCacheDir().getAbsolutePath()); + envMap.put("TMPDIR", Tools.DIR_CACHE.getAbsolutePath()); envMap.put("LIBGL_MIPMAP", "3"); // On certain GLES drivers, overloading default functions shader hack fails, so disable it @@ -191,7 +191,7 @@ public class JREUtils { envMap.put("FORCE_VSYNC", String.valueOf(LauncherPreferences.PREF_FORCE_VSYNC)); - envMap.put("MESA_GLSL_CACHE_DIR", activity.getCacheDir().getAbsolutePath()); + envMap.put("MESA_GLSL_CACHE_DIR", Tools.DIR_CACHE.getAbsolutePath()); if (LOCAL_RENDERER != null) { envMap.put("MESA_GL_VERSION_OVERRIDE", LOCAL_RENDERER.equals("opengles3_virgl")?"4.3":"4.6"); envMap.put("MESA_GLSL_VERSION_OVERRIDE", LOCAL_RENDERER.equals("opengles3_virgl")?"430":"460"); @@ -200,7 +200,7 @@ public class JREUtils { envMap.put("allow_higher_compat_version", "true"); envMap.put("allow_glsl_extension_directive_midshader", "true"); envMap.put("MESA_LOADER_DRIVER_OVERRIDE", "zink"); - envMap.put("VTEST_SOCKET_NAME", activity.getCacheDir().getAbsolutePath() + "/.virgl_test"); + envMap.put("VTEST_SOCKET_NAME", new File(Tools.DIR_CACHE, ".virgl_test").getAbsolutePath()); envMap.put("LD_LIBRARY_PATH", LD_LIBRARY_PATH); envMap.put("PATH", jreHome + "/bin:" + Os.getenv("PATH")); @@ -328,7 +328,7 @@ public class JREUtils { ArrayList overridableArguments = new ArrayList<>(Arrays.asList( "-Djava.home=" + runtimeHome, - "-Djava.io.tmpdir=" + ctx.getCacheDir().getAbsolutePath(), + "-Djava.io.tmpdir=" + Tools.DIR_CACHE.getAbsolutePath(), "-Duser.home=" + Tools.DIR_GAME_HOME, "-Duser.language=" + System.getProperty("user.language"), "-Dos.name=Linux",