From ed048fb4cbd9b0d9102dbe24c8db220fa961d4bc Mon Sep 17 00:00:00 2001 From: artdeell Date: Sat, 6 Jul 2024 16:03:51 +0300 Subject: [PATCH] Feat[launcher]: ranked Java Runtime picking from two sources --- .../java/net/kdt/pojavlaunch/NewJREUtil.java | 77 +++++++++++++++---- .../kdt/pojavlaunch/multirt/MultiRTUtils.java | 4 +- .../net/kdt/pojavlaunch/utils/MathUtils.java | 41 +++++++++- 3 files changed, 100 insertions(+), 22 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/NewJREUtil.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/NewJREUtil.java index ba90d0add..a0df3f7c5 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/NewJREUtil.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/NewJREUtil.java @@ -47,46 +47,89 @@ public class NewJREUtil { return false; } } - public static InternalRuntime getInternalRuntime(String s_runtime) { - Runtime runtime = MultiRTUtils.read(s_runtime); - if(runtime == null) return null; + + private static InternalRuntime getInternalRuntime(Runtime runtime) { for(InternalRuntime internalRuntime : InternalRuntime.values()) { if(internalRuntime.name.equals(runtime.name)) return internalRuntime; } return null; } - private static InternalRuntime findAppropriateInternalRuntime(int targetVersion) { + private static MathUtils.RankedValue getNearestInstalledRuntime(int targetVersion) { + List runtimes = MultiRTUtils.getRuntimes(); + return MathUtils.findNearestPositive(targetVersion, runtimes, (runtime)->runtime.javaVersion); + } + + private static MathUtils.RankedValue getNearestInternalRuntime(int targetVersion) { List runtimeList = Arrays.asList(InternalRuntime.values()); return MathUtils.findNearestPositive(targetVersion, runtimeList, (runtime)->runtime.majorVersion); } + /** @return true if everything is good, false otherwise. */ public static boolean installNewJreIfNeeded(Activity activity, JMinecraftVersionList.Version versionInfo) { //Now we have the reliable information to check if our runtime settings are good enough if (versionInfo.javaVersion == null || versionInfo.javaVersion.component.equalsIgnoreCase("jre-legacy")) return true; + int gameRequiredVersion = versionInfo.javaVersion.majorVersion; + LauncherProfiles.load(); + AssetManager assetManager = activity.getAssets(); MinecraftProfile minecraftProfile = LauncherProfiles.getCurrentProfile(); - String selectedRuntime = Tools.getSelectedRuntime(minecraftProfile); - Runtime runtime = MultiRTUtils.read(selectedRuntime); - if (runtime.javaVersion >= versionInfo.javaVersion.majorVersion) { + String profileRuntime = Tools.getSelectedRuntime(minecraftProfile); + Runtime runtime = MultiRTUtils.read(profileRuntime); + // Partly trust the user with his own selection, if the game can even try to run in this case + if (runtime.javaVersion >= gameRequiredVersion) { + // Check whether the selection is an internal runtime + InternalRuntime internalRuntime = getInternalRuntime(runtime); + // If it is, check if updates are available from the APK file + if(internalRuntime != null) { + // Not calling showRuntimeFail on failure here because we did, technically, find the compatible runtime + return checkInternalRuntime(assetManager, internalRuntime); + } return true; } - String appropriateRuntime = MultiRTUtils.getNearestJreName(versionInfo.javaVersion.majorVersion); - boolean failOnMiss = false; - InternalRuntime internalRuntime; - if(appropriateRuntime == null) { - internalRuntime = NewJREUtil.findAppropriateInternalRuntime(versionInfo.javaVersion.majorVersion); - failOnMiss = true; - }else { - internalRuntime = NewJREUtil.getInternalRuntime(appropriateRuntime); + // If the runtime version selected by the user is not appropriate for this version (which means the game won't run at all) + // automatically pick from either an already installed runtime, or a runtime packed with the launcher + MathUtils.RankedValue nearestInstalledRuntime = getNearestInstalledRuntime(gameRequiredVersion); + MathUtils.RankedValue nearestInternalRuntime = getNearestInternalRuntime(gameRequiredVersion); + + MathUtils.RankedValue selectedRankedRuntime = (MathUtils.RankedValue) MathUtils.multiTypeObjectMin( + nearestInternalRuntime, nearestInstalledRuntime, + (v1)->v1.rank, + (v2)->v2.rank + ); + + // No possible selections + if(selectedRankedRuntime == null) { + showRuntimeFail(activity, versionInfo); + return false; } - if((internalRuntime == null || !NewJREUtil.checkInternalRuntime(activity.getAssets(), internalRuntime)) && failOnMiss) { - showRuntimeFail(activity, versionInfo); + Object selected = selectedRankedRuntime.value; + String appropriateRuntime; + InternalRuntime internalRuntime; + + // Perform checks on the picked runtime + if(selected instanceof Runtime) { + // If it's an already installed runtime, save its name and check if + // it's actually an internal one (just in case) + Runtime selectedRuntime = (Runtime) selected; + appropriateRuntime = selectedRuntime.name; + internalRuntime = getInternalRuntime(selectedRuntime); + } else if (selected instanceof InternalRuntime) { + // If it's an internal runtime, set it's name as the appropriate one. + internalRuntime = (InternalRuntime) selected; + appropriateRuntime = internalRuntime.name; + } else { + throw new RuntimeException("Unexpected type of selected: "+selected.getClass().getName()); + } + + // If it turns out the selected runtime is actually an internal one, attempt automatic installation or update + if(internalRuntime != null && !checkInternalRuntime(assetManager, internalRuntime)) { + // Not calling showRuntimeFail here because we did, technically, find the compatible runtime return false; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/multirt/MultiRTUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/multirt/MultiRTUtils.java index 52d07063b..09e602cd4 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/multirt/MultiRTUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/multirt/MultiRTUtils.java @@ -61,7 +61,9 @@ public class MultiRTUtils { public static String getNearestJreName(int majorVersion) { List runtimes = getRuntimes(); - Runtime nearestRuntime = MathUtils.findNearestPositive(majorVersion, runtimes, (runtime)->runtime.javaVersion); + MathUtils.RankedValue nearestRankedRuntime = MathUtils.findNearestPositive(majorVersion, runtimes, (runtime)->runtime.javaVersion); + if(nearestRankedRuntime == null) return null; + Runtime nearestRuntime = nearestRankedRuntime.value; if(nearestRuntime == null) return null; return nearestRuntime.name; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/MathUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/MathUtils.java index f26b3af2e..b00b49257 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/MathUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/MathUtils.java @@ -21,11 +21,11 @@ public class MathUtils { * @param targetValue the target value * @param objects the list of objects that the search will be performed on * @param valueProvider the provider for each values - * @return the object which has the closest value to targetValue, or null if values of all + * @return the RankedValue that wraps the object which has the closest value to targetValue, or null if values of all * objects are less than targetValue * @param the object type that is used for the search. */ - public static T findNearestPositive(int targetValue, List objects, ValueProvider valueProvider) { + public static RankedValue findNearestPositive(int targetValue, List objects, ValueProvider valueProvider) { int delta = Integer.MAX_VALUE; T selectedObject = null; for(T object : objects) { @@ -33,16 +33,49 @@ public class MathUtils { if(objectValue < targetValue) continue; int currentDelta = objectValue - targetValue; - if(currentDelta == 0) return object; + if(currentDelta == 0) return new RankedValue<>(object, 0); if(currentDelta >= delta) continue; selectedObject = object; delta = currentDelta; } - return selectedObject; + if(selectedObject == null) return null; + return new RankedValue<>(selectedObject, delta); } public interface ValueProvider { int getValue(T object); } + + public static final class RankedValue { + public final T value; + public final int rank; + public RankedValue(T value, int rank) { + this.value = value; + this.rank = rank; + } + } + + /** + * Out of two objects with different types, select one with the lowest value. + * @param object1 Object 1 for comparsion + * @param object2 Object 2 for comparsion + * @param valueProvider1 Value provider for object 1 + * @param valueProvider2 Value provider for object 2 + * @return If value of object 1 is lower than or equal to object 2, returns object 1 + * Otherwise, returns object 2 + * @param Type of object 1 + * @param Type of object 2 + */ + public static Object multiTypeObjectMin(T object1, Y object2, ValueProvider valueProvider1, ValueProvider valueProvider2) { + if(object1 == null) return object2; + if(object2 == null) return object1; + int value1 = valueProvider1.getValue(object1); + int value2 = valueProvider2.getValue(object2); + if(value1 <= value2) { + return object1; + } else { + return object2; + } + } }