Refactor[jar launcher]: rework swap thread & classpath

This commit is contained in:
khanhduytran0 committed 2024-01-28 14:26:42 +07:00
1 parent d72d84dd57
commit 7829917534
7 files changed
+126 -120

No files matched your search

@@ -43,14 +43,13 @@ public class PojavLauncher {
}
});
System.setProperty("cacio.managed.screensize", args[2]);
try {
// Try to initialize Caciocavallo17
Class.forName("com.github.caciocavallosilano.cacio.ctc.CTCPreloadClassLoader");
} catch (ClassNotFoundException e) {}
if (args[0].equals("--launchJar")) {
UIKit.callback_JavaGUIViewController_launchJarFile(args[1]);
if (args[0].equals("-jar")) {
UIKit.callback_JavaGUIViewController_launchJarFile(args[1], Arrays.copyOfRange(args, 2, args.length));
} else {
launchMinecraft(args);
}
@@ -61,8 +60,9 @@ public class PojavLauncher {
System.setProperty("appdir", "./spiral");
System.setProperty("resource_dir", "./spiral/rsrc");
System.setProperty("glfw.windowSize", args[2]);
String[] size = args[2].split("x");
String sizeStr = System.getProperty("cacio.managed.screensize");
System.setProperty("glfw.windowSize", sizeStr);
String[] size = sizeStr.split("x");
MCOptionUtils.load();
MCOptionUtils.set("fullscreen", "false");
MCOptionUtils.set("overrideWidth", size[0]);
@@ -29,45 +29,7 @@ public class UIKit {
System.setProperty("os.name", osName);
}
public static void callback_JavaGUIViewController_launchJarFile(final String filepath) throws Throwable {
// Thread for refreshing the AWT buffer
new Thread(() -> {
Method getCurrentScreenRGB;
try {
try {
getCurrentScreenRGB = Class.forName("net.java.openjdk.cacio.ctc.CTCScreen").getMethod("getCurrentScreenRGB");
} catch (ClassNotFoundException e) {
getCurrentScreenRGB = Class.forName("com.github.caciocavallosilano.cacio.ctc.CTCScreen").getMethod("getCurrentScreenRGB");
}
} catch (Throwable th) {
System.err.println("Failed to find class CTCScreen");
th.printStackTrace();
System.exit(1);
return;
}
long lastTime = System.currentTimeMillis();
while (true) {
int[] pixelsArray = null;
try{
pixelsArray = (int[])getCurrentScreenRGB.invoke(null);
} catch (Throwable e) {}
if (pixelsArray != null) {
//System.out.println(java.util.Arrays.toString(pixelsArray));
refreshAWTBuffer(pixelsArray);
}
long currentTime = System.currentTimeMillis();
if (currentTime - lastTime < 16) {
try {
Thread.sleep(16 - (currentTime - lastTime));
} catch (InterruptedException e) {
break;
}
}
lastTime = currentTime;
}
}, "AWTFBRefreshThread").start();
public static void callback_JavaGUIViewController_launchJarFile(final String filepath, String[] args) throws Throwable {
// Launch the JAR file
String mainClassName = null;
@@ -78,15 +40,12 @@ public class UIKit {
throw new IllegalArgumentException("no main manifest attribute, in \"" + filepath + "\"");
}
PojavClassLoader loader = (PojavClassLoader) ClassLoader.getSystemClassLoader();
loader.addURL(new File(filepath).toURI().toURL());
// LabyMod Installer uses FlatLAF which has some macOS-specific codes, so we make it think it's running on Linux.
patch_FlatLAF_setLinux();
Class<?> clazz = loader.loadClass(mainClass);
Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass(mainClass);
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, new Object[]{new String[]{}});
method.invoke(null, new Object[]{args});
}
public static void updateMCGuiScale() {
+72 -33
View File
@@ -13,43 +13,17 @@
#define SPECIALBTN_LOGOUTPUT -100
static BOOL shouldHitEnterAfterWindowShown;
static int* rgbArray;
static SurfaceView* surfaceView;
static jclass class_CTCAndroidInput;
static jmethodID method_ReceiveInput;
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_uikit_UIKit_refreshAWTBuffer(JNIEnv* env, jclass clazz, jintArray jreRgbArray) {
if (!runtimeJNIEnvPtr) {
dispatch_async(dispatch_get_main_queue(), ^{
(*runtimeJavaVMPtr)->AttachCurrentThread(runtimeJavaVMPtr, &runtimeJNIEnvPtr, NULL);
assert(runtimeJNIEnvPtr);
});
}
int *tmpArray = (*env)->GetIntArrayElements(env, jreRgbArray, 0);
memcpy(rgbArray, tmpArray, windowWidth * windowHeight * 4);
(*env)->ReleaseIntArrayElements(env, jreRgbArray, tmpArray, JNI_ABORT);
dispatch_async(dispatch_get_main_queue(), ^{
[surfaceView displayLayer];
});
// Wait until something renders at the middle
if (shouldHitEnterAfterWindowShown && rgbArray[windowWidth/2 + windowWidth*windowHeight/2] != 0) {
shouldHitEnterAfterWindowShown = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 200 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^(void){
// Auto hit Enter to install immediately
AWTInputBridge_sendKey('\n');
});
}
}
void AWTInputBridge_nativeSendData(int type, int i1, int i2, int i3, int i4) {
if (runtimeJNIEnvPtr == NULL) {
if (!runtimeJNIEnvPtr) {
return;
}
if (method_ReceiveInput == NULL) {
if (!method_ReceiveInput) {
class_CTCAndroidInput = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "net/java/openjdk/cacio/ctc/CTCAndroidInput");
if ((*runtimeJNIEnvPtr)->ExceptionCheck(runtimeJNIEnvPtr) == JNI_TRUE) {
(*runtimeJNIEnvPtr)->ExceptionClear(runtimeJNIEnvPtr);
@@ -78,11 +52,66 @@ void AWTInputBridge_sendKey(int keycode) {
AWTInputBridge_nativeSendData(EVENT_TYPE_KEY, ' ', keycode, 0, 0);
}
@interface SurfaceView()
@interface SurfaceView() {
JNIEnv *surfaceJNIEnv;
jclass class_CTCScreen;
jmethodID method_GetRGB;
int *rgbArray;
}
@property(nonatomic) CGColorSpaceRef colorSpace;
@end
@implementation SurfaceView
- (void)refreshBuffer {
if (!runtimeJavaVMPtr) {
// JVM is not ready yet
return;
} else if (!surfaceJNIEnv) {
// Obtain JNIEnvs
(*runtimeJavaVMPtr)->AttachCurrentThread(runtimeJavaVMPtr, &surfaceJNIEnv, NULL);
assert(surfaceJNIEnv);
dispatch_async(dispatch_get_main_queue(), ^{
(*runtimeJavaVMPtr)->AttachCurrentThread(runtimeJavaVMPtr, &runtimeJNIEnvPtr, NULL);
assert(runtimeJNIEnvPtr);
});
// Obtain CTCScreen.getCurrentScreenRGB()
class_CTCScreen = (*surfaceJNIEnv)->FindClass(surfaceJNIEnv, "net/java/openjdk/cacio/ctc/CTCScreen");
if ((*surfaceJNIEnv)->ExceptionCheck(surfaceJNIEnv) == JNI_TRUE) {
(*surfaceJNIEnv)->ExceptionClear(surfaceJNIEnv);
class_CTCScreen = (*surfaceJNIEnv)->FindClass(surfaceJNIEnv, "com/github/caciocavallosilano/cacio/ctc/CTCScreen");
}
assert(class_CTCScreen != NULL);
method_GetRGB = (*surfaceJNIEnv)->GetStaticMethodID(surfaceJNIEnv, class_CTCScreen, "getCurrentScreenRGB", "()[I");
assert(method_GetRGB != NULL);
rgbArray = calloc(4, (size_t) (windowWidth * windowHeight));
}
jintArray jreRgbArray = (jintArray) (*surfaceJNIEnv)->CallStaticObjectMethod(
surfaceJNIEnv,
class_CTCScreen,
method_GetRGB
);
if (!jreRgbArray) {
return;
}
int *tmpArray = (*surfaceJNIEnv)->GetIntArrayElements(surfaceJNIEnv, jreRgbArray, 0);
memcpy(rgbArray, tmpArray, windowWidth * windowHeight * 4);
(*surfaceJNIEnv)->ReleaseIntArrayElements(surfaceJNIEnv, jreRgbArray, tmpArray, JNI_ABORT);
dispatch_async(dispatch_get_main_queue(), ^{
[surfaceView displayLayer];
});
// Wait until something renders at the middle
if (shouldHitEnterAfterWindowShown && rgbArray[windowWidth/2 + windowWidth*windowHeight/2] != 0) {
shouldHitEnterAfterWindowShown = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 200 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^(void){
// Auto hit Enter to install immediately
AWTInputBridge_sendKey('\n');
});
}
}
- (void)displayLayer {
CGDataProviderRef bitmapProvider = CGDataProviderCreateWithData(NULL, rgbArray, windowWidth * windowHeight * 4, NULL);
CGImageRef bitmap = CGImageCreate(windowWidth, windowHeight, 8, 32, 4 * windowWidth, _colorSpace, kCGImageAlphaFirst | kCGBitmapByteOrder32Little, bitmapProvider, NULL, FALSE, kCGRenderingIntentDefault);
@@ -266,9 +295,21 @@ void AWTInputBridge_sendKey(int keycode) {
self.logOutputView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:self.logOutputView];
rgbArray = calloc(4, (size_t) (windowWidth * windowHeight));
setenv("POJAV_SKIP_JNI_GLFW", "1", 1);
// Register the display loop
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:surfaceView selector:@selector(refreshBuffer)];
if (@available(iOS 15.0, tvOS 15.0, *)) {
if(getPrefBool(@"video.max_framerate")) {
displayLink.preferredFrameRateRange = CAFrameRateRangeMake(30, 120, 120);
} else {
displayLink.preferredFrameRateRange = CAFrameRateRangeMake(30, 60, 60);
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
launchJVM(nil, self.filepath, windowWidth, windowHeight, _requiredJavaVersion);
@@ -373,8 +414,6 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
}
- (void)showErrorMessage:(NSString *)message {
free(rgbArray);
rgbArray = NULL;
surfaceView = nil;
showDialog(localize(@"Error", nil), message);
}
+20 -13
View File
@@ -94,8 +94,8 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
init_loadDefaultEnv();
init_loadCustomEnv();
BOOL launchJar = NO;
NSString *gameDir;
NSString *defaultJRETag;
if ([launchTarget isKindOfClass:NSDictionary.class]) {
// Get preferred Java version from current profile
@@ -126,6 +126,7 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
} else {
defaultJRETag = @"execute_jar";
gameDir = @(getenv("POJAV_GAME_DIR"));
launchJar = YES;
}
NSLog(@"[JavaLauncher] Looking for Java %d or later", minVersion);
NSString *javaHome = getSelectedJavaHome(defaultJRETag, minVersion);
@@ -171,7 +172,9 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
margv[++margc] = [NSString stringWithFormat:@"%@/bin/java", javaHome].UTF8String;
margv[++margc] = "-XstartOnFirstThread";
margv[++margc] = "-Djava.system.class.loader=net.kdt.pojavlaunch.PojavClassLoader";
if (!launchJar) {
margv[++margc] = "-Djava.system.class.loader=net.kdt.pojavlaunch.PojavClassLoader";
}
margv[++margc] = "-Xms128M";
margv[++margc] = [NSString stringWithFormat:@"-Xmx%dM", allocmem].UTF8String;
margv[++margc] = [NSString stringWithFormat:@"-Djava.library.path=%@/Frameworks", NSBundle.mainBundle.bundlePath].UTF8String;
@@ -204,11 +207,6 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
margv[++margc] = "-XX:+UnlockExperimentalVMOptions";
margv[++margc] = "-XX:+DisablePrimordialThreadGuardPages";
// Setup Caciocavallo
margv[++margc] = "-Djava.awt.headless=false";
margv[++margc] = "-Dcacio.font.fontmanager=sun.awt.X11FontManager";
margv[++margc] = "-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler";
// Disable Forge 1.16.x early progress window
margv[++margc] = "-Dfml.earlyprogresswindow=false";
@@ -227,9 +225,14 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
return 1;
}
// Setup Caciocavallo
margv[++margc] = "-Djava.awt.headless=false";
margv[++margc] = "-Dcacio.font.fontmanager=sun.awt.X11FontManager";
margv[++margc] = "-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler";
margv[++margc] = [NSString stringWithFormat:@"-Dcacio.managed.screensize=%dx%d", width, height].UTF8String;
margv[++margc] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel";
if (isJava8) {
// Setup Caciocavallo
margv[++margc] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel";
margv[++margc] = "-Dawt.toolkit=net.java.openjdk.cacio.ctc.CTCToolkit";
margv[++margc] = "-Djava.awt.graphicsenv=net.java.openjdk.cacio.ctc.CTCGraphicsEnvironment";
} else {
@@ -237,7 +240,6 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
margv[++margc] = "--add-opens=java.base/java.net=ALL-UNNAMED";
// Setup Caciocavallo
margv[++margc] = "-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel";
margv[++margc] = "-Dawt.toolkit=com.github.caciocavallosilano.cacio.ctc.CTCToolkit";
margv[++margc] = "-Djava.awt.graphicsenv=com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment";
@@ -289,21 +291,26 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi
init_loadCustomJvmFlags(&margc, (const char **)margv);
NSLog(@"[Init] Found JLI lib");
NSString *classpath = [NSString stringWithFormat:@"%@/*", librariesPath];
if (launchJar) {
classpath = [classpath stringByAppendingFormat:@":%@", launchTarget];
}
margv[++margc] = "-cp";
margv[++margc] = [NSString stringWithFormat:@"%@/*", librariesPath].UTF8String;
margv[++margc] = classpath.UTF8String;
margv[++margc] = "net.kdt.pojavlaunch.PojavLauncher";
if (username == nil) {
margv[++margc] = "--launchJar";
if (launchJar) {
margv[++margc] = "-jar";
} else {
margv[++margc] = username.UTF8String;
}
if ([launchTarget isKindOfClass:NSDictionary.class]) {
margv[++margc] = [launchTarget[@"id"] UTF8String];
} else {
margv[++margc] = [launchTarget UTF8String];
}
margv[++margc] = [NSString stringWithFormat:@"%dx%d", width, height].UTF8String;
//margv[++margc] = "ghidra.GhidraRun";
pJLI_Launch = (JLI_Launch_func *)dlsym(libjli, "JLI_Launch");
+1
View File
@@ -173,6 +173,7 @@ static PLLogOutputView* current;
}
+ (void)handleExitCode:(int)code {
if (!current) return;
dispatch_async(dispatch_get_main_queue(), ^(void){
if (current.hidden) {
[current actionToggleLogOutput];
+24 -24
View File
@@ -102,7 +102,7 @@ hooked_ProcessImpl_forkAndExec(JNIEnv *env, jobject process, jint mode, jbyteArr
jlong hooked_ProcessHandleImpl_isAlive0(JNIEnv *env, jclass clazz, jlong jpid) {
jlong result = orig_ProcessHandleImpl_isAlive0(env, clazz, jpid);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionClear(runtimeJNIEnvPtr);
(*env)->ExceptionClear(env);
}
return result;
}
@@ -137,61 +137,61 @@ void CTCDesktopPeer_openGlobal(JNIEnv *env, jclass clazz, jstring path) {
(*env)->ReleaseStringUTFChars(env, path, stringChars);
}
void registerOpenHandler() {
void registerOpenHandler(JNIEnv *env) {
jclass cls;
// Hook forkAndExec
orig_ProcessImpl_forkAndExec = dlsym(RTLD_DEFAULT, "Java_java_lang_UNIXProcess_forkAndExec");
if (!orig_ProcessImpl_forkAndExec) {
orig_ProcessImpl_forkAndExec = dlsym(RTLD_DEFAULT, "Java_java_lang_ProcessImpl_forkAndExec");
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "java/lang/ProcessImpl");
cls = (*env)->FindClass(env, "java/lang/ProcessImpl");
} else {
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "java/lang/UNIXProcess");
cls = (*env)->FindClass(env, "java/lang/UNIXProcess");
}
JNINativeMethod forkAndExecMethod[] = {
{"forkAndExec", "(I[B[B[BI[BI[B[IZ)I", (void *)&hooked_ProcessImpl_forkAndExec}
};
(*runtimeJNIEnvPtr)->RegisterNatives(runtimeJNIEnvPtr, cls, forkAndExecMethod, 1);
(*env)->RegisterNatives(env, cls, forkAndExecMethod, 1);
// (Java 17 only) Hook isAlive0
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "java/lang/ProcessHandleImpl");
if ((*runtimeJNIEnvPtr)->ExceptionOccurred(runtimeJNIEnvPtr)) {
cls = (*env)->FindClass(env, "java/lang/ProcessHandleImpl");
if ((*env)->ExceptionOccurred(env)) {
// Java 8
(*runtimeJNIEnvPtr)->ExceptionClear(runtimeJNIEnvPtr);
(*env)->ExceptionClear(env);
} else {
orig_ProcessHandleImpl_isAlive0 = dlsym(RTLD_DEFAULT, "Java_java_lang_ProcessHandleImpl_isAlive0");
JNINativeMethod isAlive0Method[] = {
{"isAlive0", "(J)J", (void *)&hooked_ProcessHandleImpl_isAlive0}
};
(*runtimeJNIEnvPtr)->RegisterNatives(runtimeJNIEnvPtr, cls, isAlive0Method, 1);
(*env)->RegisterNatives(env, cls, isAlive0Method, 1);
}
// Register CTCClipboard natives
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "net/java/openjdk/cacio/ctc/CTCClipboard");
if ((*runtimeJNIEnvPtr)->ExceptionOccurred(runtimeJNIEnvPtr)) {
cls = (*env)->FindClass(env, "net/java/openjdk/cacio/ctc/CTCClipboard");
if ((*env)->ExceptionOccurred(env)) {
// Java 17
(*runtimeJNIEnvPtr)->ExceptionClear(runtimeJNIEnvPtr);
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "com/github/caciocavallosilano/cacio/ctc/CTCClipboard");
(*env)->ExceptionClear(env);
cls = (*env)->FindClass(env, "com/github/caciocavallosilano/cacio/ctc/CTCClipboard");
}
JNINativeMethod clipboardMethods[] = {
{"nQuerySystemClipboard", "()V", (void *)&CTCClipboard_nQuerySystemClipboard},
{"nPutClipboardData", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&CTCClipboard_nPutClipboardData}
};
(*runtimeJNIEnvPtr)->RegisterNatives(runtimeJNIEnvPtr, cls, clipboardMethods, 2);
(*env)->RegisterNatives(env, cls, clipboardMethods, 2);
// Register CTCDesktopPeer natives
cls = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "net/java/openjdk/cacio/ctc/CTCDesktopPeer");
if ((*runtimeJNIEnvPtr)->ExceptionOccurred(runtimeJNIEnvPtr)) {
cls = (*env)->FindClass(env, "net/java/openjdk/cacio/ctc/CTCDesktopPeer");
if ((*env)->ExceptionOccurred(env)) {
// Java 17, not available
//(*runtimeJNIEnvPtr)->ExceptionDescribe(runtimeJNIEnvPtr);
(*runtimeJNIEnvPtr)->ExceptionClear(runtimeJNIEnvPtr);
//(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return;
}
JNINativeMethod peerOpenMethods[] = {
{"openFile", "(Ljava/lang/String;)V", (void *)&CTCDesktopPeer_openGlobal},
{"openUri", "(Ljava/lang/String;)V", (void *)&CTCDesktopPeer_openGlobal}
};
(*runtimeJNIEnvPtr)->RegisterNatives(runtimeJNIEnvPtr, cls, peerOpenMethods, 2);
(*env)->RegisterNatives(env, cls, peerOpenMethods, 2);
}
// JNI_OnLoad
@@ -206,11 +206,11 @@ void JNI_OnLoadGLFW() {
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
runtimeJavaVMPtr = vm;
(*runtimeJavaVMPtr)->GetEnv(runtimeJavaVMPtr, (void **)&runtimeJNIEnvPtr, JNI_VERSION_1_4);
registerOpenHandler();
if (getenv("POJAV_SKIP_JNI_GLFW")) {
runtimeJNIEnvPtr = nil;
} else {
JNIEnv *env;
(*runtimeJavaVMPtr)->GetEnv(runtimeJavaVMPtr, (void **)&env, JNI_VERSION_1_4);
registerOpenHandler(env);
if (!getenv("POJAV_SKIP_JNI_GLFW")) {
runtimeJNIEnvPtr = env;
JNI_OnLoadGLFW();
}
+1 -1
View File
@@ -11,7 +11,7 @@ void (*orig_exit)(int code);
int (*orig_open)(const char *path, int oflag, ...);
void handle_fatal_exit(int code) {
if (NSThread.isMainThread || !SurfaceViewController.isRunning) {
if (NSThread.isMainThread) {
return;
}