diff --git a/.run/Pojlib [build].run.xml b/.run/Pojlib [build].run.xml index 23baabc1..0d1d7875 100644 --- a/.run/Pojlib [build].run.xml +++ b/.run/Pojlib [build].run.xml @@ -18,7 +18,29 @@ true true false - false + false + + + + + + + + true + true + false + false \ No newline at end of file diff --git a/src/main/assets/JRE-22.zip b/src/main/assets/JRE-22.zip deleted file mode 100644 index 2ff1b32e..00000000 Binary files a/src/main/assets/JRE-22.zip and /dev/null differ diff --git a/src/main/assets/lwjgl/version b/src/main/assets/lwjgl/version index bfcf683d..5deb1884 100644 --- a/src/main/assets/lwjgl/version +++ b/src/main/assets/lwjgl/version @@ -1 +1 @@ -1730783037751 \ No newline at end of file +1730879022018 \ No newline at end of file diff --git a/src/main/java/pojlib/API.java b/src/main/java/pojlib/API.java index 9a3c3e72..cebc508d 100644 --- a/src/main/java/pojlib/API.java +++ b/src/main/java/pojlib/API.java @@ -33,8 +33,8 @@ public class API { public static boolean finishedDownloading = true; public static boolean ignoreInstanceName; public static boolean customRAMValue = false; - public static double downloadStatus; - public static String currentDownload; + public static double downloadStatus = 0; + public static String currentDownload = ""; public static String profileImage; public static String profileName; public static String profileUUID; diff --git a/src/main/java/pojlib/APIHandler.java b/src/main/java/pojlib/APIHandler.java index 2188cece..bbb926f3 100644 --- a/src/main/java/pojlib/APIHandler.java +++ b/src/main/java/pojlib/APIHandler.java @@ -15,7 +15,8 @@ import java.util.stream.Collectors; import pojlib.util.Constants; -import pojlib.util.DownloadUtils; +import pojlib.util.download.DownloadManager; +import pojlib.util.download.DownloadUtils; import pojlib.util.GsonUtils; import pojlib.util.Logger; @@ -112,7 +113,7 @@ public static T postFullUrl(String url, HashMap query, T bod public static String[] getQCSupportedVersions() { File versionsJson = new File(Constants.USER_HOME + "/supportedVersions.json"); try { - DownloadUtils.downloadFile(SUPPORTED_VERSIONS, versionsJson); + DownloadUtils.downloadFile(SUPPORTED_VERSIONS, versionsJson, new DownloadManager(1)); } catch (IOException e) { Logger.getInstance().appendToLog("Error while grabbing supported versions!\n" + e); } diff --git a/src/main/java/pojlib/UnityPlayerActivity.java b/src/main/java/pojlib/UnityPlayerActivity.java index 019b07a8..1a9776e2 100644 --- a/src/main/java/pojlib/UnityPlayerActivity.java +++ b/src/main/java/pojlib/UnityPlayerActivity.java @@ -49,6 +49,8 @@ import pojlib.util.Constants; import pojlib.util.FileUtil; import pojlib.util.Logger; +import pojlib.util.download.DownloadManager; +import pojlib.util.download.DownloadUtils; public class UnityPlayerActivity extends ActivityGroup implements IUnityPlayerLifecycleEvents, GrabListener { @@ -94,16 +96,6 @@ protected String updateUnityCommandLineArguments(String cmdLine) getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); mUnityPlayer.requestFocus(); - File jre = new File(this.getFilesDir() + "/runtimes/JRE-22"); - if (!jre.exists()) { - FileUtil.unzipArchiveFromAsset(this, "JRE-22.zip", this.getFilesDir() + "/runtimes/JRE-22"); - try { - Files.copy(Paths.get(this.getApplicationInfo().nativeLibraryDir + "/libawt_xawt.so"), Paths.get(this.getFilesDir() + "/runtimes/JRE-22/lib/libawt_xawt.so")); - } catch (IOException e) { - e.printStackTrace(); - } - } - updateWindowSize(this); GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); diff --git a/src/main/java/pojlib/install/Installer.java b/src/main/java/pojlib/install/Installer.java index 3efbc7c5..ad137d09 100644 --- a/src/main/java/pojlib/install/Installer.java +++ b/src/main/java/pojlib/install/Installer.java @@ -9,13 +9,16 @@ import org.apache.commons.io.FileUtils; import pojlib.APIHandler; +import pojlib.util.download.DownloadManager; +import pojlib.util.download.DownloadUtils; import pojlib.util.json.MinecraftInstances; import pojlib.util.*; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Map; -import java.util.Objects; import java.util.StringJoiner; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -34,7 +37,7 @@ public static String installClient(VersionInfo minecraftVersionInfo, String game for (int i = 0; i < 5; i++) { if (i == 4) throw new RuntimeException("Client download failed after 5 retries"); - if (!clientFile.exists()) DownloadUtils.downloadFile(minecraftVersionInfo.downloads.client.url, clientFile); + if (!clientFile.exists()) DownloadUtils.downloadFile(minecraftVersionInfo.downloads.client.url, clientFile, new DownloadManager(1)); if (DownloadUtils.compareSHA1(clientFile, minecraftVersionInfo.downloads.client.sha1)) return clientFile.getAbsolutePath(); } return null; @@ -44,7 +47,6 @@ public static String installClient(VersionInfo minecraftVersionInfo, String game // Returns the classpath of the downloaded libraries public static String installLibraries(VersionInfo versionInfo, String gameDir) throws IOException { Logger.getInstance().appendToLog("Downloading Libraries for: " + versionInfo.id); - StringJoiner classpath = new StringJoiner(File.pathSeparator); for (VersionInfo.Library library : versionInfo.libraries) { if(library.name.contains("lwjgl")) { @@ -63,7 +65,7 @@ public static String installLibraries(VersionInfo versionInfo, String gameDir) t sha1 = APIHandler.getRaw(library.url + path + ".sha1"); if (!libraryFile.exists()) { Logger.getInstance().appendToLog("Downloading: " + library.name); - DownloadUtils.downloadFile(library.url + path, libraryFile); + DownloadUtils.downloadFile(library.url + path, libraryFile, new DownloadManager(1)); } } else { VersionInfo.Library.Artifact artifact = library.downloads.artifact; @@ -71,7 +73,7 @@ public static String installLibraries(VersionInfo versionInfo, String gameDir) t sha1 = artifact.sha1; if (!libraryFile.exists()) { Logger.getInstance().appendToLog("Downloading: " + library.name); - DownloadUtils.downloadFile(artifact.url, libraryFile); + DownloadUtils.downloadFile(artifact.url, libraryFile, new DownloadManager(1)); } } @@ -96,10 +98,13 @@ public static String installAssets(VersionInfo minecraftVersionInfo, String game Logger.getInstance().appendToLog("Downloading assets"); JsonObject assets = APIHandler.getFullUrl(minecraftVersionInfo.assetIndex.url, JsonObject.class); + int totalAssets = assets.getAsJsonObject("objects").size(); + DownloadManager downloadManager = new DownloadManager(totalAssets); + ThreadPoolExecutor tp = new ThreadPoolExecutor(8, 8, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); for (Map.Entry entry : assets.getAsJsonObject("objects").entrySet()) { - AsyncDownload thread = new AsyncDownload(entry, minecraftVersionInfo, gameDir); + AsyncDownload thread = new AsyncDownload(entry, gameDir, downloadManager); tp.execute(thread); } @@ -108,7 +113,21 @@ public static String installAssets(VersionInfo minecraftVersionInfo, String game while (!tp.awaitTermination(100, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) {} - DownloadUtils.downloadFile(minecraftVersionInfo.assetIndex.url, new File(gameDir + "/assets/indexes/" + minecraftVersionInfo.assets + ".json")); + File jre = new File(activity.getFilesDir() + "/runtimes/JRE-22"); + String jreURL = "https://github.com/QuestCraftPlusPlus/android-openjdk-build-multiarch/releases/download/jre22-6.0.0/JRE-22.zip"; + try { + if (!jre.exists()) { + File jreZip = new File(activity.getFilesDir() + "/runtimes/JRE-22.zip"); + DownloadUtils.downloadFile(jreURL, jreZip, new DownloadManager(1)); + FileUtil.unzipArchive(jreZip.getPath(), activity.getFilesDir() + "/runtimes/JRE-22"); + Files.copy(Paths.get(activity.getApplicationInfo().nativeLibraryDir + "/libawt_xawt.so"), Paths.get(activity.getFilesDir() + "/runtimes/JRE-22/lib/libawt_xawt.so")); + jreZip.delete(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + DownloadUtils.downloadFile(minecraftVersionInfo.assetIndex.url, new File(gameDir + "/assets/indexes/" + minecraftVersionInfo.assets + ".json"), downloadManager); FileUtils.writeByteArrayToFile(new File(instance.gameDir + "/config/sodium-options.json"), FileUtil.loadFromAssetToByte(activity, "sodium-options.json")); FileUtils.writeByteArrayToFile(new File(instance.gameDir + "/config/vivecraft-config.properties"), FileUtil.loadFromAssetToByte(activity, "vivecraft-config.properties")); @@ -125,42 +144,47 @@ public static String installAssets(VersionInfo minecraftVersionInfo, String game } public static class AsyncDownload implements Runnable { - Map.Entry entry; - VersionInfo versionInfo; - String gameDir; + private final Map.Entry entry; + private final String gameDir; + private final DownloadManager downloadManager; + private final String fileName; + public AsyncDownload(Map.Entry entry, String gameDir, DownloadManager downloadManager) { + this.entry = entry; + this.gameDir = gameDir; + this.downloadManager = downloadManager; + this.fileName = entry.getKey(); + } + + @Override public void run() { VersionInfo.Asset asset = new Gson().fromJson(entry.getValue(), VersionInfo.Asset.class); String path = asset.hash.substring(0, 2) + "/" + asset.hash; File assetFile = new File(gameDir + "/assets/objects/", path); for (int i = 0; i < 5; i++) { - if (i == 4) throw new RuntimeException(String.format("Asset download of %s failed after 5 retries", entry.getKey())); + if (i == 4) throw new RuntimeException(String.format("Asset download of %s failed after 5 retries", fileName)); if (!assetFile.exists()) { - Logger.getInstance().appendToLog("Downloading: " + entry.getKey()); + Logger.getInstance().appendToLog("Downloading: " + fileName); try { - DownloadUtils.downloadFile(Constants.MOJANG_RESOURCES_URL + "/" + path, assetFile); + DownloadUtils.downloadFile(Constants.MOJANG_RESOURCES_URL + "/" + path, assetFile, downloadManager); } catch (IOException e) { throw new RuntimeException(e); } } if (DownloadUtils.compareSHA1(assetFile, asset.hash)) { + downloadManager.fileDownloadComplete(fileName); break; } else { assetFile.delete(); } } } - - public AsyncDownload( Map.Entry entry, VersionInfo versionInfo, String gameDir) { - this.entry = entry; - this.versionInfo = versionInfo; - this.gameDir = gameDir; - } } + //Used for mod libraries, vanilla is handled a different (tbh better) way private static String parseLibraryNameToPath(String libraryName) { String[] parts = libraryName.split(":"); diff --git a/src/main/java/pojlib/util/StreamDL.java b/src/main/java/pojlib/util/StreamDL.java deleted file mode 100644 index 1e49a9c7..00000000 --- a/src/main/java/pojlib/util/StreamDL.java +++ /dev/null @@ -1,35 +0,0 @@ -package pojlib.util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; - -public class StreamDL extends InputStream { - - private final InputStream in; - private int count; - private final Collection listeners = new ArrayList(); - - StreamDL(InputStream in) { - this.in = in; - } - - @Override - public int read() throws IOException { - int b = in.read(); - byteReceived(b); - return b; - } - - public void addListener(StreamListener listener) { - listeners.add(listener); - } - - private void byteReceived(int b) { - for (StreamListener l: listeners) { - l.byteReceived(b, ++count); - } - } - -} diff --git a/src/main/java/pojlib/util/download/DownloadManager.java b/src/main/java/pojlib/util/download/DownloadManager.java new file mode 100644 index 00000000..b3231658 --- /dev/null +++ b/src/main/java/pojlib/util/download/DownloadManager.java @@ -0,0 +1,38 @@ +package pojlib.util.download; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import pojlib.API; + +public class DownloadManager { + private final Map downloadProgress = new ConcurrentHashMap<>(); + private final int totalFiles; + private final AtomicInteger completedFiles = new AtomicInteger(0); + + public DownloadManager(int totalFiles) { + this.totalFiles = totalFiles; + } + + public void updateProgress(String fileName, double progress) { + downloadProgress.put(fileName, progress); + API.currentDownload = fileName; + getOverallProgress(); + } + + public void fileDownloadComplete(String fileName) { + completedFiles.incrementAndGet(); + downloadProgress.remove(fileName); + getOverallProgress(); + } + + private void getOverallProgress() { + int completed = completedFiles.get(); + double overallProgress = ((double) completed / totalFiles) * 100; + if (completed == totalFiles) {API.currentDownload = "Finished! Ready to start.";} + API.downloadStatus = overallProgress; + } +} + + diff --git a/src/main/java/pojlib/util/DownloadUtils.java b/src/main/java/pojlib/util/download/DownloadUtils.java similarity index 70% rename from src/main/java/pojlib/util/DownloadUtils.java rename to src/main/java/pojlib/util/download/DownloadUtils.java index 7df79bea..6d6399f2 100644 --- a/src/main/java/pojlib/util/DownloadUtils.java +++ b/src/main/java/pojlib/util/download/DownloadUtils.java @@ -1,9 +1,10 @@ -package pojlib.util; +package pojlib.util.download; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; -import pojlib.API; + +import pojlib.util.Logger; import javax.net.ssl.SSLException; import java.io.*; @@ -14,8 +15,7 @@ import javax.annotation.Nullable; public class DownloadUtils { - - private static void download(URL url, OutputStream os) throws IOException { + private static void download(URL url, OutputStream os, String fileName, DownloadManager downloadManager) throws IOException { final int MAX_RETRIES = 3; int attempts = 0; @@ -26,27 +26,14 @@ private static void download(URL url, OutputStream os) throws IOException { conn.setConnectTimeout(10000); conn.setDoInput(true); conn.connect(); - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - try (InputStream is = new StreamDL(conn.getInputStream())) { - String[] segments = url.getPath().split("/"); - API.currentDownload = segments[segments.length - 1]; - - ((StreamDL)is).addListener((b, count) -> { - - if (b == -1) { - API.downloadStatus = 0; - API.currentDownload = null; - } else { - API.downloadStatus = count * 0.000001; - } - - }); + if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { + int totalBytes = conn.getContentLength(); // Get the total size of the file + try (InputStream is = new StreamDL(conn.getInputStream(), downloadManager, fileName, totalBytes)) { IOUtils.copy(is, os); } return; } - } catch (IOException e) { if (++attempts >= MAX_RETRIES || e instanceof SSLException) { throw new IOException("Unable to download from " + url, e); @@ -55,13 +42,12 @@ private static void download(URL url, OutputStream os) throws IOException { } } - public static void downloadFile(String url, File out) throws IOException { + public static void downloadFile(String url, File out, DownloadManager downloadManager) throws IOException { Objects.requireNonNull(out.getParentFile()).mkdirs(); File tempOut = File.createTempFile(out.getName(), ".part", out.getParentFile()); try { - OutputStream bos2 = new BufferedOutputStream(Files.newOutputStream(tempOut.toPath())); - try { - download(new URL(url), bos2); + try (OutputStream bos2 = new BufferedOutputStream(Files.newOutputStream(tempOut.toPath()))) { + download(new URL(url), bos2, out.getName(), downloadManager); tempOut.renameTo(out); bos2.close(); if (tempOut.exists()) tempOut.delete(); @@ -70,9 +56,9 @@ public static void downloadFile(String url, File out) throws IOException { if (tempOut.exists()) tempOut.delete(); throw th2; } - } catch (IOException th3) { + } catch (IOException e) { if (tempOut.exists()) tempOut.delete(); - throw th3; + throw e; } } diff --git a/src/main/java/pojlib/util/download/StreamDL.java b/src/main/java/pojlib/util/download/StreamDL.java new file mode 100644 index 00000000..05f29239 --- /dev/null +++ b/src/main/java/pojlib/util/download/StreamDL.java @@ -0,0 +1,48 @@ +package pojlib.util.download; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; + +public class StreamDL extends InputStream { + private final InputStream in; + private int count; + private final Collection listeners = new ArrayList<>(); + private final DownloadManager downloadManager; + private final String fileName; + private final int totalBytes; + + public StreamDL(InputStream in, DownloadManager downloadManager, String fileName, int totalBytes) { + this.in = in; + this.downloadManager = downloadManager; + this.fileName = fileName; + this.totalBytes = totalBytes; + } + + @Override + public int read() throws IOException { + int b = in.read(); + byteReceived(b); + return b; + } + + public void addListener(StreamListener listener) { + listeners.add(listener); + } + + private void byteReceived(int b) { + if (b != -1) { + count++; + double progress = ((double) count / totalBytes) * 100; + downloadManager.updateProgress(fileName, progress); + } else { + downloadManager.fileDownloadComplete(fileName); + } + + for (StreamListener l : listeners) { + l.byteReceived(b, count); + } + } +} + diff --git a/src/main/java/pojlib/util/StreamListener.java b/src/main/java/pojlib/util/download/StreamListener.java similarity index 81% rename from src/main/java/pojlib/util/StreamListener.java rename to src/main/java/pojlib/util/download/StreamListener.java index 59722cc9..fdb3bed2 100644 --- a/src/main/java/pojlib/util/StreamListener.java +++ b/src/main/java/pojlib/util/download/StreamListener.java @@ -1,4 +1,4 @@ -package pojlib.util; +package pojlib.util.download; import java.util.EventListener; diff --git a/src/main/java/pojlib/util/json/MinecraftInstances.java b/src/main/java/pojlib/util/json/MinecraftInstances.java index c211576c..5f1d2844 100644 --- a/src/main/java/pojlib/util/json/MinecraftInstances.java +++ b/src/main/java/pojlib/util/json/MinecraftInstances.java @@ -3,7 +3,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -11,10 +10,9 @@ import pojlib.account.MinecraftAccount; import pojlib.API; import pojlib.InstanceHandler; -import pojlib.account.Msa; import pojlib.util.Constants; -import pojlib.util.DownloadUtils; -import pojlib.util.FileUtil; +import pojlib.util.download.DownloadManager; +import pojlib.util.download.DownloadUtils; import pojlib.util.GsonUtils; import pojlib.util.Logger; @@ -80,9 +78,9 @@ private ModsJson parseModsJson(String jsonPath) { private ModsJson downloadCurrentModsJson(String userHome) throws Exception { File mods = new File(userHome + "/new_mods.json"); if(API.developerMods) { - DownloadUtils.downloadFile(InstanceHandler.DEV_MODS, mods); + DownloadUtils.downloadFile(InstanceHandler.DEV_MODS, mods, new DownloadManager(1)); } else { - DownloadUtils.downloadFile(InstanceHandler.MODS, mods); + DownloadUtils.downloadFile(InstanceHandler.MODS, mods, new DownloadManager(1)); } return parseModsJson(mods.getAbsolutePath()); @@ -173,7 +171,7 @@ private void updateModByType(List newMods) throws IOException { newMod.slug + (newMod.type.equals("resourcepack") ? ".zip" : ".jar") ); if(!mod.exists() || !extMod.version.equals(newMod.version)) { - DownloadUtils.downloadFile(newMod.download_link, mod); + DownloadUtils.downloadFile(newMod.download_link, mod, new DownloadManager(1)); extMod = newMod; break; } @@ -184,7 +182,7 @@ private void updateModByType(List newMods) throws IOException { extMod.slug + (extMod.type.equals("resourcepack") ? ".zip" : ".jar") ); if(!mod.exists()) { - DownloadUtils.downloadFile(extMod.download_link, mod); + DownloadUtils.downloadFile(extMod.download_link, mod, new DownloadManager(1)); } } newExtMods.add(extMod); @@ -194,12 +192,13 @@ private void updateModByType(List newMods) throws IOException { } private void downloadAllMods(List newMods) throws IOException { + DownloadManager downloadManager = new DownloadManager(newMods.size()); for(ProjectInfo newMod : newMods) { File mod = new File( gameDir + (newMod.type.equals("mod") ? "/mods" : "/resourcepacks"), newMod.slug + (newMod.type.equals("resourcepack") ? ".zip" : ".jar") ); - DownloadUtils.downloadFile(newMod.download_link, mod); + DownloadUtils.downloadFile(newMod.download_link, mod, downloadManager); } extProjects = newMods.toArray(new ProjectInfo[0]); diff --git a/src/main/jni/libopenxr_loader.so b/src/main/jni/libopenxr_loader.so index 35d27bee..4566e333 100644 --- a/src/main/jni/libopenxr_loader.so +++ b/src/main/jni/libopenxr_loader.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:547bc81d961b6830c6e2e6d19fb417cf583ebcba0819bb26f84d9fbabf8acb97 -size 1583672 +oid sha256:3c99b192c4f37c70240163377d960b57e8a05ea746464d7e0b6c94c4ec55ea0c +size 1579592 diff --git a/src/test/java/pojlib/util/StreamDLTest.java b/src/test/java/pojlib/util/StreamDLTest.java deleted file mode 100644 index 4c456cd1..00000000 --- a/src/test/java/pojlib/util/StreamDLTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package pojlib.util; - -import org.apache.commons.io.IOUtils; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.file.Files; - -class StreamDLTest { - - private static void download(URL url, OutputStream os) throws IOException { - InputStream is = null; - try { - // System.out.println("Connecting: " + url.toString()); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setConnectTimeout(10000); - conn.setDoInput(true); - conn.connect(); - if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { - throw new IOException("Server returned HTTP " + conn.getResponseCode() - + ": " + conn.getResponseMessage()); - } - is = new StreamDL(conn.getInputStream()); - - ((StreamDL)is).addListener((b, count) -> { - System.out.println(b); - System.out.println(count); - }); - - IOUtils.copy(is, os); - } catch (IOException e) { - throw new IOException("Unable to download from " + url, e); - } finally { - if (is != null) { - try { - is.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - - public static void downloadFile(String url, File out) throws IOException { - out.getParentFile().mkdirs(); - File tempOut = File.createTempFile(out.getName(), ".part", out.getParentFile()); - BufferedOutputStream bos = null; - try { - OutputStream bos2 = new BufferedOutputStream(Files.newOutputStream(tempOut.toPath())); - try { - download(new URL(url), bos2); - tempOut.renameTo(out); - bos2.close(); - if (tempOut.exists()) tempOut.delete(); - - } catch (IOException th2) { - if (tempOut.exists()) tempOut.delete(); - throw th2; - } - } catch (IOException th3) { - if (tempOut.exists()) tempOut.delete(); - throw th3; - } - } - -}