Feat[launcher]: ranked Java Runtime picking from two sources

This commit is contained in:
artdeell
2024-07-06 16:03:51 +03:00
committed by Maksim Belov
parent 181f81131f
commit ed048fb4cb
3 changed files with 100 additions and 22 deletions

View File

@@ -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<Runtime> getNearestInstalledRuntime(int targetVersion) {
List<Runtime> runtimes = MultiRTUtils.getRuntimes();
return MathUtils.findNearestPositive(targetVersion, runtimes, (runtime)->runtime.javaVersion);
}
private static MathUtils.RankedValue<InternalRuntime> getNearestInternalRuntime(int targetVersion) {
List<InternalRuntime> 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<Runtime> nearestInstalledRuntime = getNearestInstalledRuntime(gameRequiredVersion);
MathUtils.RankedValue<InternalRuntime> 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;
}

View File

@@ -61,7 +61,9 @@ public class MultiRTUtils {
public static String getNearestJreName(int majorVersion) {
List<Runtime> runtimes = getRuntimes();
Runtime nearestRuntime = MathUtils.findNearestPositive(majorVersion, runtimes, (runtime)->runtime.javaVersion);
MathUtils.RankedValue<Runtime> 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;
}

View File

@@ -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 <T> the object type that is used for the search.
*/
public static <T> T findNearestPositive(int targetValue, List<T> objects, ValueProvider<T> valueProvider) {
public static <T> RankedValue<T> findNearestPositive(int targetValue, List<T> objects, ValueProvider<T> 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<T> {
int getValue(T object);
}
public static final class RankedValue<T> {
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 <T> Type of object 1
* @param <Y> Type of object 2
*/
public static <T,Y> Object multiTypeObjectMin(T object1, Y object2, ValueProvider<T> valueProvider1, ValueProvider<Y> 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;
}
}
}