Robust downloadString() cache

This commit is contained in:
BuildTools 2023-06-29 19:41:30 +03:00
parent 63dec3fb2a
commit c0aec8178b
7 changed files with 116 additions and 30 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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<String> downloadLoaderVersionList(boolean onlyStable) throws IOException {
String loaderMetadata = DownloadUtils.downloadString(FABRIC_LOADER_METADATA_URL);
List<String> 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<String> 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;
}
}
}

View File

@ -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<String> 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);

View File

@ -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> T downloadStringCached(String url, String cacheName, ParseCallback<T> 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> {
T process(String input) throws ParseException;
}
public static class ParseException extends Exception {
public ParseException(Exception e) {
super(e);
}
}
}

View File

@ -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<String> 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",