Merge branch 'v3_openjdk' of https://github.com/khanhduytran0/PojavLauncher.git into v3_openjdk

This commit is contained in:
khanhduytran0 2020-09-06 06:43:10 +07:00
commit 9bcf2de57c
41 changed files with 994 additions and 459 deletions

27
.github/workflows/android.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Android CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: |
chmod +x gradlew
./gradlew clean assembleDebug
./gradlew test
# should test???
- name: Upload APK
uses: actions/upload-artifact@v2
with:
name: app-debug
path: app/build/outputs/apk/debug/app-debug.apk

View File

@ -50,9 +50,9 @@ deploy:
api-key: $GITHUB_API_KEY
file: $TRAVIS_BUILD_DIR/app/build/outputs/apk/debug/app-debug.apk
skip_cleanup: true
name: PojavLauncher_DevBuild-$TRAVIS_COMMIT
name: PojavLauncher-v3_DevBuild-$TRAVIS_COMMIT
body: Automatic build of PojavLauncher from the latest source commit ($TRAVIS_COMMIT) built by Travis CI on $(date +'%F %T %Z').
prerelease: true
overwrite: true
target_commitish: $TRAVIS_COMMIT
on: [push]
# on: [push]

View File

@ -1,3 +1,4 @@
![Android CI](https://github.com/PojavLauncherTeam/PojavLauncher/workflows/Android%20CI/badge.svg)
# PojavLauncher
A Minecraft: Java Edition launcher for Android based from Boardwalk. Support up-to Minecraft 1.11.2
@ -6,7 +7,8 @@ A Minecraft: Java Edition launcher for Android based from Boardwalk. Support up-
## Current status
- [x] **Removed** ~~BinaryExecutor: execute `java` binary, no `JNIInvocation`.~~
- [x] JVDroid OpenJDK 11 (32 and 64-bit ARM and x86). Partial, no error `can't lock mutex`, but now exit with none output.
- [x] **Temporary removed** ~~JVDroid OpenJDK 11 (32 and 64-bit ARM and x86). Partial, no error `can't lock mutex`, but now exit with none output.~~
- [x] OpenJDK 9 Mobile port
- [ ] AWT/Swing for mod installer. Will use `Caciocavallo` project.
- [ ] OpenGL in OpenJDK environment. Use Boardwalk 2 method or other.
- [ ] OpenAL 64-bit version

View File

@ -3,11 +3,22 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 26
dexOptions {
javaMaxHeapSize "4g"
javaMaxHeapSize "4g"
}
lintOptions {
abortOnError false
}
lintOptions {
abortOnError false
}
signingConfigs {
customDebug {
storeFile file("debug.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
defaultConfig {
applicationId "net.kdt.pojavlaunch"
minSdkVersion 21
@ -15,11 +26,21 @@ android {
versionCode 156235
versionName "3.2.0_6401b_20200822"
multiDexEnabled true //important
ndk {
abiFilters "armeabi-v7a"
}
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.customDebug
}
release {
// Don't set to true or java.awt will be a.a or something similar.
// Don't set to true or java.awt will be a.a or something similar.
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// defaultConfig already set
@ -28,6 +49,12 @@ android {
}
}
externalNativeBuild {
ndkBuild {
path file('src/main/jni/Android.mk')
}
}
// Keep the following configuration in order to might make Minecraft 1.12 support.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8

BIN
app/debug.keystore Normal file

Binary file not shown.

View File

@ -41,6 +41,11 @@
android:name=".MCLauncherActivity"
android:configChanges="keyboardHidden|orientation|screenSize"/>
<activity
android:screenOrientation="sensorLandscape"
android:name=".InstallModActivity"
android:configChanges="keyboardHidden|orientation|screenSize"/>
<activity
android:screenOrientation="sensorLandscape"
android:name=".CustomControlsActivity"

Binary file not shown.

View File

@ -1,44 +0,0 @@
package net.kdt.pojavlaunch;
import android.system.*;
import java.io.*;
// This clads should be named as 'LoadMe' as original
public class BinaryExecutor
{
private BinaryExecutor() {}
public static void initJavaRuntime() {
dlopen(Tools.homeJreDir + "/lib/jli/libjli.so");
dlopen(Tools.homeJreDir + "/lib/server/libjvm.so");
dlopen(Tools.homeJreDir + "/lib/libverify.so");
dlopen(Tools.homeJreDir + "/lib/libjava.so");
dlopen(Tools.homeJreDir + "/lib/libnet.so");
dlopen(Tools.homeJreDir + "/lib/libnio.so");
dlopen(Tools.homeJreDir + "/lib/libawt.so");
dlopen(Tools.homeJreDir + "/lib/libawt_headless.so");
}
public static FileDescriptor redirectStdio() throws ErrnoException {
File logFile = new File(Tools.MAIN_PATH, "v3log.txt");
FileDescriptor fd = Os.open(logFile.getAbsolutePath(), OsConstants.O_WRONLY | OsConstants.O_CREAT | OsConstants.O_TRUNC, 0666);
Os.dup2(fd, OsConstants.STDERR_FILENO);
Os.dup2(fd, OsConstants.STDOUT_FILENO);
return fd;
}
public static native int chdir(String path);
public static native boolean dlopen(String libPath);
public static native void setLdLibraryPath(String ldLibraryPath);
public static native void setupBridgeEGL();
// BEFORE Load and execute PIE binary using dlopen and dlsym("main")
// AFTER: Execute a binary in forked process
public static native int executeBinary(String[] args);
static {
System.loadLibrary("binexecutor");
}
}

View File

@ -0,0 +1,112 @@
package net.kdt.pojavlaunch;
import android.graphics.*;
import android.os.*;
import android.support.v7.app.*;
import android.view.*;
import com.oracle.dalvik.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class InstallModActivity extends AppCompatActivity
{
private TextureView mTextureView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_mod);
final File modFile = (File) getIntent().getExtras().getSerializable("modFile");
mTextureView = findViewById(R.id.installmod_surfaceview);
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){
@Override
public void onSurfaceTextureAvailable(SurfaceTexture tex, int w, int h) {
try {
Surface surface = new Surface(tex);
Field field = surface.getClass().getDeclaredField("mNativeObject");
field.setAccessible(true);
JREUtils.setupBridgeSurfaceAWT((long) field.get(surface));
} catch (Throwable th) {
Tools.showError(InstallModActivity.this, th, true);
}
new Thread(new Runnable(){
@Override
public void run() {
launchJavaRuntime(modFile);
finish();
}
}).start();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture tex) {
return false;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture tex, int w, int h) {
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture tex) {
}
});
}
public void forceClose(View v) {
MainActivity.dialogForceClose(this);
}
private void launchJavaRuntime(File modFile) {
try {
List<String> javaArgList = new ArrayList<String>();
javaArgList.add(Tools.homeJreDir + "/bin/java");
// javaArgList.add("-Xms512m");
javaArgList.add("-Xmx512m");
javaArgList.add("-Djava.home=" + Tools.homeJreDir);
javaArgList.add("-Djava.io.tmpdir=" + getCacheDir().getAbsolutePath());
javaArgList.add("-Dos.name=Linux");
File cacioAwtLibPath = new File(Tools.MAIN_PATH, "cacioawtlib");
if (cacioAwtLibPath.exists()) {
StringBuilder libStr = new StringBuilder();
for (File file: cacioAwtLibPath.listFiles()) {
if (file.getName().endsWith(".jar")) {
libStr.append(":" + file.getAbsolutePath());
}
}
javaArgList.add("-Xbootclasspath/a" + libStr.toString());
}
File cacioArgOverrideFile = new File(cacioAwtLibPath, "overrideargs.txt");
if (cacioArgOverrideFile.exists()) {
javaArgList.addAll(Arrays.asList(Tools.read(cacioArgOverrideFile.getAbsolutePath()).split(" ")));
}
javaArgList.add("-jar");
javaArgList.add(modFile.getAbsolutePath());
System.out.println(Arrays.toString(javaArgList.toArray(new String[0])));
JREUtils.setJavaEnvironment(this);
JREUtils.redirectStdio();
JREUtils.setJavaEnvironment(this);
JREUtils.initJavaRuntime();
JREUtils.chdir(Tools.MAIN_PATH);
VMLauncher.launchJVM(javaArgList.toArray(new String[0]));
} catch (Throwable th) {
Tools.showError(this, th, true);
}
}
}

View File

@ -0,0 +1,83 @@
package net.kdt.pojavlaunch;
import android.system.*;
import java.io.*;
import android.content.*;
public class JREUtils
{
private JREUtils() {}
public static void initJavaRuntime() {
dlopen(Tools.homeJreDir + "/lib/jli/libjli.so");
dlopen(Tools.homeJreDir + "/lib/server/libjvm.so");
dlopen(Tools.homeJreDir + "/lib/libverify.so");
dlopen(Tools.homeJreDir + "/lib/libjava.so");
dlopen(Tools.homeJreDir + "/lib/libnet.so");
dlopen(Tools.homeJreDir + "/lib/libnio.so");
dlopen(Tools.homeJreDir + "/lib/libawt.so");
dlopen(Tools.homeJreDir + "/lib/libawt_headless.so");
}
public static void redirectStdio() throws ErrnoException {
File logFile = new File(Tools.MAIN_PATH, "latestlog.txt");
FileDescriptor fd = Os.open(logFile.getAbsolutePath(), OsConstants.O_WRONLY | OsConstants.O_CREAT | OsConstants.O_TRUNC, 0666);
Os.dup2(fd, OsConstants.STDERR_FILENO);
Os.dup2(fd, OsConstants.STDOUT_FILENO);
// return fd;
}
public static void setJavaEnvironment(Context ctx) throws IOException, ErrnoException {
String libName = System.getProperty("os.arch").contains("64") ? "lib64" : "lib";
String ldLibraryPath = (
// To make libjli.so ignore re-execute
Tools.homeJreDir + "/lib/server:" +
"/system/" + libName + ":" +
"/vendor/" + libName + ":" +
"/vendor/" + libName + "/hw:" +
ctx.getApplicationInfo().nativeLibraryDir + ":" +
Tools.homeJreDir + "/lib/jli:" +
Tools.homeJreDir + "/lib"
);
setEnvironment("JAVA_HOME", Tools.homeJreDir);
setEnvironment("HOME", Tools.MAIN_PATH);
setEnvironment("TMPDIR", ctx.getCacheDir().getAbsolutePath());
// setEnvironment("LIBGL_MIPMAP", "3");
setEnvironment("MESA_GLSL_CACHE_DIR", ctx.getCacheDir().getAbsolutePath());
setEnvironment("LD_LIBRARY_PATH", ldLibraryPath);
setEnvironment("PATH", Tools.homeJreDir + "/bin:" + Os.getenv("PATH"));
setLdLibraryPath(ldLibraryPath);
// return ldLibraryPath;
}
private static void setEnvironment(String name, String value) throws ErrnoException, IOException {
if (MainActivity.LAUNCH_TYPE == MainActivity.LTYPE_PROCESS) {
MainActivity.mLaunchShell.writeToProcess("export " + name + "=" + value);
} else {
Os.setenv(name, value, true);
}
}
public static native int chdir(String path);
public static native boolean dlopen(String libPath);
public static native void setLdLibraryPath(String ldLibraryPath);
public static native void setupBridgeWindow(Object surface);
public static native void setupBridgeSurfaceAWT(long surface);
// BEFORE Load and execute PIE binary using dlopen and dlsym("main")
// AFTER: Execute a binary in forked process
public static native int executeBinary(String[] args);
static {
System.loadLibrary("pojavexec");
}
}

View File

@ -755,22 +755,19 @@ public class MCLauncherActivity extends AppCompatActivity
@Override
public void onClick(DialogInterface p1, int p2)
{
switch(p2){
case 0:{ // Mods manager
modManager();
} break;
case 1:{ // OptiFine installer
installOptiFine();
} break;
case 2:{ // Custom controls
if (Tools.enableDevFeatures) {
startActivity(new Intent(MCLauncherActivity.this, CustomControlsActivity.class));
}
} break;
case 3:{ // Settings
startActivity(new Intent(MCLauncherActivity.this, LauncherPreferenceActivity.class));
} break;
case 4:{ // About
switch (p2) {
case 0: // Mod installer
installMod();
break;
case 1: // Custom controls
if (Tools.enableDevFeatures) {
startActivity(new Intent(MCLauncherActivity.this, CustomControlsActivity.class));
}
break;
case 2: // Settings
startActivity(new Intent(MCLauncherActivity.this, LauncherPreferenceActivity.class));
break;
case 3:{ // About
final AlertDialog.Builder aboutB = new AlertDialog.Builder(MCLauncherActivity.this);
aboutB.setTitle(R.string.mcl_option_about);
try
@ -792,70 +789,11 @@ public class MCLauncherActivity extends AppCompatActivity
builder.show();
}
public void modManager()
{
/*
File file1 = new File(Tools.mpModEnable);
File file2 = new File(Tools.mpModDisable);
File file3 = new File(Tools.mpModAddNewMo);
file1.mkdirs();
file2.mkdir();
try
{
file3.createNewFile();
}
catch (IOException e){}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Mods manager (Forge)");
builder.setPositiveButton(android.R.string.cancel, null);
AlertDialog dialog = builder.create();
MFileListView flv = new MFileListView(this, dialog);
flv.listFileAt(Tools.datapath + "/ModsManager");
flv.setFileSelectedListener(new MFileSelectedListener(){
@Override
public void onFileLongClick(File file, String path, String nane, String extension)
{
// TODO: Implement this method
}
@Override
public void onFileSelected(File file, String path, String nane, String extension)
{
// TODO: Implement this method
if(extension.equals(".jar")) {
} else {
openSelectMod();
}
}
});
dialog.setView(flv);
dialog.show();
*/
Tools.dialogOnUiThread(this, "Mods manager", "This feature is not yet supported!");
}
public void openSelectMod()
{
private void installMod() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.alerttitle_installmod);
builder.setPositiveButton(android.R.string.cancel, null);
AlertDialog dialog = builder.create();
FileListView flv = new FileListView(this);
dialog.setView(flv);
dialog.show();
}
private void installOptiFine() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.alerttitle_installoptifine);
builder.setPositiveButton(android.R.string.cancel, null);
final AlertDialog dialog = builder.create();
FileListView flv = new FileListView(this);
flv.setFileSelectedListener(new FileSelectedListener(){
@ -863,7 +801,9 @@ public class MCLauncherActivity extends AppCompatActivity
@Override
public void onFileSelected(File file, String path, String name) {
if (name.endsWith(".jar")) {
doInstallOptiFine(file);
Intent intent = new Intent(MCLauncherActivity.this, InstallModActivity.class);
intent.putExtra("modFile", file);
startActivity(intent);
dialog.dismiss();
}
}
@ -872,143 +812,6 @@ public class MCLauncherActivity extends AppCompatActivity
dialog.show();
}
private void doInstallOptiFine(File optifineFile) {
new OptiFineInstaller().execute(optifineFile);
}
private class OptiFineInstaller extends AsyncTask<File, String, Throwable>
{
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(MCLauncherActivity.this);
dialog.setTitle("Installing OptiFine");
dialog.setMessage("Preparing");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMax(5);
dialog.setCancelable(false);
dialog.show();
}
@Override
protected Throwable doInBackground(File[] file) {
final StringBuilder currentLog = new StringBuilder();
LoggerJava.LoggerOutputStream logOut = new LoggerJava.LoggerOutputStream(System.out, new LoggerJava.OnStringPrintListener(){
@Override
public void onCharPrint(char c)
{
currentLog.append(c);
}
});
LoggerJava.LoggerOutputStream logErr = new LoggerJava.LoggerOutputStream(System.err, new LoggerJava.OnStringPrintListener(){
@Override
public void onCharPrint(char c)
{
currentLog.append(c);
}
});
Throwable throwable = null;
File convertedFile = null;
try {
publishProgress("Preparing", "5");
String origMd5 = OptiFinePatcher.calculateMD5(file[0]);
convertedFile = new File(Tools.optifineDir, origMd5 + ".jar");
if (!convertedFile.exists()) {
publishProgress("Patching OptiFine Installer", null, "1", "true");
Tools.extractAssetFolder(MCLauncherActivity.this, "optifine_patch", Tools.optifineDir, true);
new File(Tools.optifineDir + "/optifine_patch/AndroidOptiFineUtilities.class.patch").delete();
String[] output = Tools.patchOptifineInstaller(MCLauncherActivity.this, file[0]);
File patchedFile = new File(output[1]);
publishProgress("Converting OptiFine", null, null, "false");
System.setOut(new PrintStream(logOut));
System.setErr(new PrintStream(logErr));
if (!convertedFile.exists()) {
RuntimeException dxError = new RuntimeException(getResources().getString(R.string.error_convert_lib, "OptiFine") + "\n" + currentLog.toString());
dxError.setStackTrace(new StackTraceElement[0]);
throw dxError;
}
patchedFile.delete();
}
publishProgress("Launching OptiFine installer", null, null, "true");
File optDir = getDir("dalvik-cache", 0);
optDir.mkdir();
DexClassLoader loader = new DexClassLoader(convertedFile.getAbsolutePath(), optDir.getAbsolutePath(), getApplicationInfo().nativeLibraryDir, getClass().getClassLoader());
// AndroidOptiFineUtilities.originalOptifineJar = convertedFile.getAbsolutePath();
Class installerClass = loader.loadClass("optifine.AndroidInstaller");
Method installerMethod = installerClass.getDeclaredMethod("doInstall", File.class);
installerMethod.invoke(null, new File(Tools.MAIN_PATH));
publishProgress("(4/5) Patching OptiFine Tweaker", null, null);
File optifineLibFile = new File("unimpl");
if (!optifineLibFile.exists()) {
throw new FileNotFoundException(optifineLibFile.getAbsolutePath() + "\n\n--- OptiFine installer log ---\n" + currentLog.toString());
}
new OptiFinePatcher(optifineLibFile).saveTweaker();
convertedFile.delete();
publishProgress("(5/5) Done!", null, null);
Thread.sleep(500);
} catch (Throwable th) {
throwable = th;
} finally {
System.setOut(logOut.getRootStream());
System.setErr(logErr.getRootStream());
/*
if (throwable != null && convertedFile != null) {
convertedFile.delete();
}
*/
return throwable;
}
}
/*
private Object fromConfig(DexClassLoader loader, String name) throws ReflectiveOperationException {
Field f = loader.loadClass("Config").getDeclaredField(name);
f.setAccessible(true);
return f.get(null);
}
*/
@Override
protected void onProgressUpdate(String[] text) {
super.onProgressUpdate(text);
dialog.setMessage(text[0]);
if (text.length > 1 && text[1] != null) {
dialog.setMax(Integer.valueOf(text[1]));
} if (text.length > 2) {
dialog.setProgress(dialog.getProgress() + 1);
} if (text.length > 3 && text[3] != null) {
dialog.setIndeterminate(Boolean.getBoolean(text[3]));
}
}
@Override
protected void onPostExecute(Throwable th) {
super.onPostExecute(th);
dialog.dismiss();
new RefreshVersionListTask().execute();
if (th == null) {
Toast.makeText(MCLauncherActivity.this, R.string.toast_optifine_success, Toast.LENGTH_SHORT).show();
} else {
Tools.showError(MCLauncherActivity.this, th);
}
}
}
private class ViewPagerAdapter extends FragmentPagerAdapter {
List<Fragment> fragmentList = new ArrayList<>();

View File

@ -68,7 +68,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
}
}
};
private MinecraftGLView glSurfaceView;
private MinecraftGLView minecraftGLView;
private int guiScale;
private DisplayMetrics displayMetrics;
public boolean hiddenTextIgnoreUpdate = true;
@ -110,6 +110,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
private Button[] controlButtons;
private File logFile;
private PrintStream logStream;
/*
@ -122,10 +123,10 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
private boolean isLogAllow = false;
// private int navBarHeight = 40;
private static final int LTYPE_PROCESS = 0;
private static final int LTYPE_INVOCATION = 1;
private static final int LTYPE_CREATEJAVAVM = 2;
private static final int LAUNCH_TYPE;
public static final int LTYPE_PROCESS = 0;
public static final int LTYPE_INVOCATION = 1;
public static final int LTYPE_CREATEJAVAVM = 2;
public static final int LAUNCH_TYPE;
static {
int launchTypeFinal = LTYPE_INVOCATION;
@ -155,7 +156,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
setContentView(R.layout.main);
try {
File logFile = new File(Tools.MAIN_PATH, "latestlog.txt");
logFile = new File(Tools.MAIN_PATH, "latestlog.txt");
logFile.delete();
logFile.createNewFile();
logStream = new PrintStream(logFile.getAbsolutePath());
@ -175,52 +176,6 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
}
});
ExitManager.setExitTrappedListener(new ExitManager.ExitTrappedListener(){
@Override
public void onExitTrapped()
{
logStream.close();
runOnUiThread(new Runnable(){
@Override
public void run() {
isExited = true;
AlertDialog.Builder d = new AlertDialog.Builder(MainActivity.this);
d.setTitle(R.string.mcn_exit_title);
try {
File crashLog = Tools.lastFileModified(Tools.crashPath);
if(crashLog != null && Tools.read(crashLog.getAbsolutePath()).startsWith("---- Minecraft Crash Report ----")){
d.setMessage(R.string.mcn_exit_crash);
} else {
fullyExit();
return;
}
} catch (Throwable th) {
d.setMessage(getStr(R.string.mcn_exit_errcrash) + "\n" + Log.getStackTraceString(th));
}
d.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface p1, int p2)
{
fullyExit();
}
});
d.setCancelable(false);
d.show();
}
});
}
});
try {
ExitManager.disableSystemExit();
} catch (Throwable th) {
Log.w(Tools.APP_NAME, "Could not disable System.exit() method!", th);
}
mProfile = PojavProfile.getCurrentProfileContent(this);
mVersionInfo = Tools.getVersionInfo(mProfile.getVersion());
@ -259,7 +214,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.nav_forceclose: dialogForceClose();
case R.id.nav_forceclose: dialogForceClose(MainActivity.this);
break;
case R.id.nav_viewlog: openLogOutput();
break;
@ -330,22 +285,18 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
this.toggleControlButton.setOnClickListener(this);
this.zoomButton.setVisibility(mVersionInfo.optifineLib == null ? View.GONE : View.VISIBLE);
this.glSurfaceView = (MinecraftGLView) findViewById(R.id.main_game_render_view);
this.minecraftGLView = (MinecraftGLView) findViewById(R.id.main_game_render_view);
ControlButton[] specialButtons = ControlButton.getSpecialButtons();
specialButtons[0].specialButtonListener = new View.OnClickListener(){
@Override
public void onClick(View p1)
{
public void onClick(View view) {
showKeyboard();
}
};
specialButtons[1].specialButtonListener = new View.OnClickListener(){
@Override
public void onClick(View view)
{
public void onClick(View view) {
MainActivity.this.onClick(toggleControlButton);
}
};
@ -382,11 +333,11 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
if (isPointerCaptureSupported()) {
if (!AndroidDisplay.grab && isCapturing) {
pointerSurface.releaseCapture(); // glSurfaceView.releasePointerCapture();
pointerSurface.releaseCapture(); // minecraftGLView.releasePointerCapture();
isCapturing = false;
} else if (AndroidDisplay.grab && !isCapturing) {
glSurfaceView.requestFocus();
pointerSurface.requestCapture(); // glSurfaceView.requestPointerCapture();
minecraftGLView.requestFocus();
pointerSurface.requestCapture(); // minecraftGLView.requestPointerCapture();
isCapturing = true;
}
}
@ -465,9 +416,9 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
// System.loadLibrary("Regal");
glSurfaceView.setFocusable(true);
glSurfaceView.setFocusableInTouchMode(true);
glSurfaceView.setEGLContextClientVersion(2);
minecraftGLView.setFocusable(true);
minecraftGLView.setFocusableInTouchMode(true);
// minecraftGLView.setEGLContextClientVersion(2);
glTouchListener = new OnTouchListener(){
private boolean isTouchInHotbar = false;
@ -478,7 +429,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
// System.out.println("Pre touch, isTouchInHotbar=" + Boolean.toString(isTouchInHotbar) + ", action=" + MotionEvent.actionToString(e.getActionMasked()));
int x = ((int) e.getX()) / scaleFactor;
int y = (glSurfaceView.getHeight() - ((int) e.getY())) / scaleFactor;
int y = (minecraftGLView.getHeight() - ((int) e.getY())) / scaleFactor;
int hudKeyHandled = handleGuiBar(x, y, e);
if (!AndroidDisplay.grab && gestureDetector.onTouchEvent(e)) {
if (hudKeyHandled != -1) {
@ -660,7 +611,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
};
if (isPointerCaptureSupported()) {
this.pointerSurface = new PointerOreoWrapper(glSurfaceView);
this.pointerSurface = new PointerOreoWrapper(minecraftGLView);
this.pointerSurface.setOnCapturedPointerListener(new PointerOreoWrapper.OnCapturedPointerListener(){
@Override
public boolean onCapturedPointer(View view, MotionEvent event) {
@ -669,7 +620,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
});
}
glSurfaceView.setOnHoverListener(new View.OnHoverListener(){
minecraftGLView.setOnHoverListener(new View.OnHoverListener(){
@Override
public boolean onHover(View p1, MotionEvent p2) {
if (!AndroidDisplay.grab && mIsResuming) {
@ -678,62 +629,56 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
return true;
}
});
glSurfaceView.setOnTouchListener(glTouchListener);
glSurfaceView.setRenderer(new GLTextureView.Renderer() {
@Override
public void onSurfaceDestroyed(GL10 gl) {
Log.d(Tools.APP_NAME, "Surface destroyed.");
}
minecraftGLView.setOnTouchListener(glTouchListener);
minecraftGLView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){
private boolean isCalled = false;
@Override
public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig p2)
{
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
AndroidDisplay.windowWidth = width;
AndroidDisplay.windowHeight = height;
calculateMcScale();
EGL10 egl10 = (EGL10) EGLContext.getEGL();
AndroidContextImplementation.theEgl = egl10;
AndroidContextImplementation.context = egl10.eglGetCurrentContext();
AndroidContextImplementation.display = egl10.eglGetCurrentDisplay();
AndroidContextImplementation.read = egl10.eglGetCurrentSurface(EGL10.EGL_READ);
AndroidContextImplementation.draw = egl10.eglGetCurrentSurface(EGL10.EGL_DRAW);
// Should we do that?
if (!isCalled) {
isCalled = true;
System.out.println(new StringBuffer().append("Gave up context: ").append(AndroidContextImplementation.context).toString());
JREUtils.setupBridgeWindow(new Surface(texture));
// Does it required anymore?
// AndroidDisplay.windowWidth += navBarHeight;
new Thread(new Runnable(){
if (LAUNCH_TYPE != LTYPE_PROCESS) {
BinaryExecutor.setupBridgeEGL();
egl10.eglMakeCurrent(AndroidContextImplementation.display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
}
new Thread(new Runnable(){
@Override
public void run() {
try {
Thread.sleep(200);
runCraft();
} catch (Throwable e) {
Tools.showError(MainActivity.this, e, true);
@Override
public void run() {
try {
Thread.sleep(200);
runCraft();
} catch (Throwable e) {
Tools.showError(MainActivity.this, e, true);
}
}
}
}).start();
}).start();
}
}
@Override
public void onDrawFrame(GL10 gl) {
//mkToast("onDrawFrame");
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
AndroidDisplay.windowWidth = width / scaleFactor;
AndroidDisplay.windowHeight = height / scaleFactor;
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
AndroidDisplay.windowWidth = width;
AndroidDisplay.windowHeight = height;
calculateMcScale();
// TODO: Implement this method for GLFW window size callback
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
});
glSurfaceView.setPreserveEGLContextOnPause(true);
glSurfaceView.setRenderMode(MinecraftGLView.RENDERMODE_CONTINUOUSLY);
// glSurfaceView.requestRender();
} catch (Throwable e) {
e.printStackTrace();
Tools.showError(this, e, true);
@ -790,7 +735,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
public void onResume() {
super.onResume();
mIsResuming = true;
if (glSurfaceView != null) glSurfaceView.requestRender();
// if (minecraftGLView != null) minecraftGLView.requestRender();
final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
final View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(uiOptions);
@ -800,8 +745,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
/*
if (hasFocus && glSurfaceView.getVisibility() == View.GONE) {
glSurfaceView.setVisibility(View.VISIBLE);
if (hasFocus && minecraftGLView.getVisibility() == View.GONE) {
minecraftGLView.setVisibility(View.VISIBLE);
}
*/
}
@ -884,11 +829,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
}
public static void fullyExit() {
if (!ExitManager.isExiting()) {
ExitManager.enableSystemExit();
System.exit(0);
}
ExitManager.stopExitLoop();
System.exit(0);
}
public void forceUserHome(String s) throws Exception {
@ -1001,14 +942,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
return args;
}
private ShellProcessOperation mLaunchShell;
private void setEnvironment(String name, String value) throws ErrnoException, IOException {
if (LAUNCH_TYPE == LTYPE_PROCESS) {
mLaunchShell.writeToProcess("export " + name + "=" + value);
} else {
Os.setenv(name, value, true);
}
}
public static ShellProcessOperation mLaunchShell;
private static void startStrace(int pid) throws Exception {
String[] straceArgs = new String[] {"/system/bin/strace",
@ -1029,16 +963,12 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
// javaArgList.add("-Xms512m");
javaArgList.add("-Xmx512m");
/*
javaArgList.add("-Djava.library.path=" +
// TODO lwjgl2 vs lwjgl3 native path
getApplicationInfo().nativeLibraryDir
);
*/
javaArgList.add("-Djava.home=" + Tools.homeJreDir);
javaArgList.add("-Djava.io.tmpdir=" + getCacheDir().getAbsolutePath());
javaArgList.add("-Dos.name=Linux");
// javaArgList.add("-Dorg.lwjgl.libname=liblwjgl3.so");
// javaArgList.add("-Dorg.lwjgl.system.jemalloc.libname=libjemalloc.so");
javaArgList.add("-Dorg.lwjgl.opengl.libname=libgl04es.so");
// javaArgList.add("-Dorg.lwjgl.opengl.libname=libRegal.so");
@ -1052,6 +982,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
javaArgList.add("-Dglfwstub.windowWidth=" + AndroidDisplay.windowWidth);
javaArgList.add("-Dglfwstub.windowHeight=" + AndroidDisplay.windowHeight);
javaArgList.add("-Dglfwstub.initEgl=false");
if (mVersionInfo.arguments != null) {
// Minecraft 1.13+
@ -1092,35 +1024,11 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
mLaunchShell.initInputStream(this);
}
String libName = System.getProperty("os.arch").contains("64") ? "lib64" : "lib";
String ldLibraryPath = (
// To make libjli.so ignore re-execute
Tools.homeJreDir + "/lib/server:" +
"/system/" + libName + ":" +
"/vendor/" + libName + ":" +
"/vendor/" + libName + "/hw:" +
getApplicationInfo().nativeLibraryDir + ":" +
Tools.homeJreDir + "/lib/jli:" +
Tools.homeJreDir + "/lib"
// "$JAVA_HOME/lib:$JAVA_HOME/lib/jli:$JAVA_HOME/lib/server"
);
setEnvironment("JAVA_HOME", Tools.homeJreDir);
setEnvironment("HOME", Tools.MAIN_PATH);
setEnvironment("TMPDIR", getCacheDir().getAbsolutePath());
// setEnvironment("LIBGL_MIPMAP", "3");
setEnvironment("MESA_GLSL_CACHE_DIR", getCacheDir().getAbsolutePath());
setEnvironment("LD_LIBRARY_PATH", ldLibraryPath);
setEnvironment("PATH", Tools.homeJreDir + "/bin:" + Os.getenv("PATH"));
// can fix java?
// setEnvironment("ORIGIN", Tools.homeJreDir + "/lib");
JREUtils.setJavaEnvironment(this);
if (LAUNCH_TYPE == LTYPE_PROCESS) {
mLaunchShell.writeToProcess("cd $HOME");
@ -1130,8 +1038,23 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
Tools.showError(this, new ErrnoException("java", exitCode), false);
}
} else { // Type Invocation
final FileDescriptor logFile = BinaryExecutor.redirectStdio();
// Is it need?
/*
Os.dup2(FileDescriptor.err, OsConstants.STDERR_FILENO);
Os.dup2(FileDescriptor.out, OsConstants.STDOUT_FILENO);
*/
JREUtils.redirectStdio();
// DEPRECATED constructor (String) api 29
/*
FileObserver fobs = new FileObserver(logFile.getAbsolutePath(), FileObserver.MODIFY){
@Override
public void onEvent(int event, String str) {
}
};
fobs.startWatching();
*/
new Thread(new Runnable() {
@Override
public void run() {
@ -1148,9 +1071,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
}
}, "RuntimeLogThread").start();
BinaryExecutor.setLdLibraryPath(ldLibraryPath);
BinaryExecutor.initJavaRuntime();
BinaryExecutor.chdir(Tools.MAIN_PATH);
JREUtils.initJavaRuntime();
JREUtils.chdir(Tools.MAIN_PATH);
if (new File(Tools.MAIN_PATH, "strace.txt").exists()) {
startStrace(android.os.Process.myTid());
@ -1314,9 +1236,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
((Button) view).setText(isVis ? R.string.control_mouseoff: R.string.control_mouseon);
}
public void dialogForceClose()
{
new AlertDialog.Builder(this)
public static void dialogForceClose(Context ctx) {
new AlertDialog.Builder(ctx)
.setMessage(R.string.mcn_exit_confirm)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
@ -1350,6 +1271,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
@Override
public void onBackPressed() {
// Prevent back
// Catch back as Esc keycode at another place
}
public void hideKeyboard() {
@ -1365,7 +1287,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener,
public void showKeyboard() {
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
glSurfaceView.requestFocus();
minecraftGLView.requestFocus();
}
private void setRightOverride(boolean val) {

View File

@ -5,7 +5,7 @@ import android.util.*;
import android.view.*;
import com.kdt.glsupport.*;
public class MinecraftGLView extends GLTextureView
public class MinecraftGLView extends TextureView
{
// private View.OnTouchListener mTouchListener;
public MinecraftGLView(Context context) {
@ -17,18 +17,5 @@ public class MinecraftGLView extends GLTextureView
super(context, attributeSet);
//setPreserveEGLContextOnPause(true);
}
@Override
public void setOnTouchListener(View.OnTouchListener l)
{
super.setOnTouchListener(l);
// mTouchListener = l;
}
@Override
public void setOnClickListener(View.OnClickListener l)
{
super.setOnClickListener(l);
}
}

View File

@ -84,6 +84,7 @@ public final class Tools
String[] classpath = generateLibClasspath(info);
// Debug: LWJGL 3 override
File lwjgl2Folder = new File(Tools.MAIN_PATH, "lwjgl2");
File lwjgl3Folder = new File(Tools.MAIN_PATH, "lwjgl3");
if (info.arguments != null && lwjgl3Folder.exists()) {
for (File file: lwjgl3Folder.listFiles()) {
@ -91,6 +92,12 @@ public final class Tools
libStr.append(file.getAbsolutePath() + ":");
}
}
} else if (lwjgl2Folder.exists()) {
for (File file: lwjgl2Folder.listFiles()) {
if (file.getName().endsWith(".jar")) {
libStr.append(file.getAbsolutePath() + ":");
}
}
}
if (isClientFirst) {

View File

@ -0,0 +1,14 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Link GLESv2 for test
LOCAL_LDLIBS := -ldl -llog -landroid -lEGL -lGLESv2
LOCAL_MODULE := pojavexec
LOCAL_CFLAGS += -DGLES_TEST
LOCAL_SRC_FILES := \
egl_bridge.c \
jre_launcher.c \
utils.c
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,5 @@
# NDK_TOOLCHAIN_VERSION := 4.9
APP_PLATFORM := android-21
APP_STL := system
APP_ABI := armeabi-v7a
# x86 arm64-v8a x86_64

View File

@ -0,0 +1,170 @@
#include <jni.h>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <EGL/egl.h>
#ifdef GLES_TEST
#include <GLES2/gl2.h>
#endif
#include <android/native_window.h>
#include <android/native_window_jni.h>
struct PotatoBridge {
ANativeWindow* androidWindow;
void* androidDisplay;
void* eglContext;
void* eglDisplay;
void* eglSurface;
/*
void* eglSurfaceRead;
void* eglSurfaceDraw;
*/
};
struct PotatoBridge potatoBridge;
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setupBridgeWindow(JNIEnv* env, jclass clazz, jobject surface) {
potatoBridge.androidDisplay = EGL_DEFAULT_DISPLAY;
potatoBridge.androidWindow = ANativeWindow_fromSurface(env, surface);
}
// Called from JNI_OnLoad of liblwjgl_opengl32
void pojav_openGLOnLoad() {
}
void pojav_openGLOnUnload() {
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglInit(JNIEnv* env, jclass clazz) {
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglMakeCurrent(JNIEnv* env, jclass clazz) {
printf("EGLBridge: Initializing\n");
printf("ANativeWindow pointer = %p\n", potatoBridge.androidWindow);
potatoBridge.eglDisplay = eglGetDisplay(potatoBridge.androidDisplay);
if (potatoBridge.eglDisplay == EGL_NO_DISPLAY) {
printf("Error: eglGetDefaultDisplay() failed: %p\n", eglGetError());
return JNI_FALSE;
}
eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (!eglInitialize(potatoBridge.eglDisplay, NULL, NULL)) {
printf("Error: eglInitialize() failed\n");
return JNI_FALSE;
}
static const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
static const EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLConfig config;
EGLint num_configs;
EGLint vid;
if (!eglChooseConfig(potatoBridge.eglDisplay, attribs, &config, 1, &num_configs)) {
printf("Error: couldn't get an EGL visual config\n");
return JNI_FALSE;
}
assert(config);
assert(num_configs > 0);
if (!eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_NATIVE_VISUAL_ID, &vid)) {
printf("Error: eglGetConfigAttrib() failed\n");
return JNI_FALSE;
}
eglBindAPI(EGL_OPENGL_ES_API);
potatoBridge.eglContext = eglCreateContext(potatoBridge.eglDisplay, config, EGL_NO_CONTEXT, ctx_attribs);
if (!potatoBridge.eglContext) {
printf("Error: eglCreateContext failed\n");
return JNI_FALSE;
}
// test eglQueryContext()
{
EGLint val;
eglQueryContext(potatoBridge.eglDisplay, potatoBridge.eglContext, EGL_CONTEXT_CLIENT_VERSION, &val);
assert(val == 2);
}
potatoBridge.eglSurface = eglCreateWindowSurface(potatoBridge.eglDisplay, config, potatoBridge.androidWindow, NULL);
if (!potatoBridge.eglSurface) {
printf("Error: eglCreateWindowSurface failed: %p\n", eglGetError());
return JNI_FALSE;
}
// sanity checks
{
EGLint val;
assert(eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_SURFACE_TYPE, &val));
assert(val & EGL_WINDOW_BIT);
}
/*
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglMakeCurrent(JNIEnv* env, jclass clazz) {
*/
printf("EGLBridge: Making current\n");
printf("EGLContext=%p, EGLDisplay=%p, EGLSurface=%p\n",
potatoBridge.eglContext,
potatoBridge.eglDisplay,
potatoBridge.eglSurface
);
EGLBoolean success = eglMakeCurrent(potatoBridge.eglDisplay, potatoBridge.eglSurface, potatoBridge.eglSurface, potatoBridge.eglContext);
if (success == EGL_FALSE) {
printf("Error: eglMakeCurrent() failed: %p\n", eglGetError());
}
// Test
#ifdef GLES_TEST
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(potatoBridge.eglDisplay, potatoBridge.eglSurface);
#endif
return success == EGL_TRUE ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglTerminate(JNIEnv* env, jclass clazz) {
printf("EGLBridge: Terminating\n");
eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(potatoBridge.eglDisplay, potatoBridge.eglSurface);
eglDestroyContext(potatoBridge.eglDisplay, potatoBridge.eglContext);
eglTerminate(potatoBridge.eglDisplay);
eglReleaseThread();
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglSwapBuffers(JNIEnv *env, jclass clazz) {
return eglSwapBuffers(potatoBridge.eglDisplay, potatoBridge.eglSurface);
}
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglSwapInterval(JNIEnv *env, jclass clazz, jint interval) {
return eglSwapInterval(potatoBridge.eglDisplay, interval);
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#include <dlfcn.h>
// Boardwalk: missing include
#include <string.h>
#include "log.h"
#include "utils.h"
// PojavLancher: fixme: are these wrong?
#define FULL_VERSION "1.9.0-internal"
#define DOT_VERSION "1.9"
typedef jint JNI_CreateJavaVM_func(JavaVM **pvm, void **penv, void *args);
typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* dot version defined */
const char* pname, /* program name */
const char* lname, /* launcher name */
jboolean javaargs, /* JAVA_ARGS */
jboolean cpwildcard, /* classpath wildcard*/
jboolean javaw, /* windows-only javaw */
jint ergo /* ergonomics class policy */
);
JavaVM *runtimeJavaVMPtr = NULL;;
JNIEnv *runtimeJNIEnvPtr = NULL;
JavaVM *dalvikJavaVMPtr = NULL;
JNIEnv *dalvikJNIEnvPtr = NULL;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
if (dalvikJavaVMPtr == NULL) {
//Save dalvik global JavaVM pointer
dalvikJavaVMPtr = vm;
LOGD("JNI_OnLoad calling GetEnv()");
JNIEnv* env = NULL;
(*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4);
/* Boardwalk: not used
LOGD("JNI_OnLoad calling initDalvikProxySelectorData()");
initDalvikProxySelectorData(env);
*/
}
LOGD("JNI_OnLoad returning()");
return JNI_VERSION_1_4;
}
static void logArgs(int argc, char** argv) {
/* BlockLauncher: disable logging
int i;
for (i = 0; i < argc; i++) {
LOGD("arg[%d]: %s", i, argv[i]);
}
*/
}
JNIEXPORT jint JNICALL Java_com_oracle_dalvik_VMLauncher_createLaunchMainJVM(JNIEnv *env, jclass clazz, jobjectArray vmArgArr, jstring mainClassStr, jobjectArray mainArgArr) {
void *libjvm = dlopen("libjvm.so", RTLD_NOW + RTLD_GLOBAL);
if (libjvm == NULL) {
LOGE("dlopen failed to open libjvm.so (dlerror %s).", dlerror());
return -1;
}
JNI_CreateJavaVM_func *jl_JNI_CreateJavaVM = (JNI_CreateJavaVM_func *) dlsym(libjvm, "JNI_CreateJavaVM");
if (jl_JNI_CreateJavaVM == NULL) {
LOGE("dlsym failed to get JNI_CreateJavaVM (dlerror %s).", dlerror());
return -1;
}
int vm_argc = (*env)->GetArrayLength(env, vmArgArr);
char **vm_argv = convert_to_char_array(env, vmArgArr);
int main_argc = (*env)->GetArrayLength(env, mainArgArr);
char **main_argv = convert_to_char_array(env, mainArgArr);
JavaVMInitArgs vm_args;
JavaVMOption options[vm_argc];
for (int i = 0; i < vm_argc; i++) {
options[i].optionString = vm_argv[i];
}
vm_args.version = JNI_VERSION_1_6;
vm_args.options = options;
vm_args.nOptions = vm_argc;
vm_args.ignoreUnrecognized = JNI_FALSE;
jint res = (jint) jl_JNI_CreateJavaVM(&runtimeJavaVMPtr, (void**)&runtimeJNIEnvPtr, &vm_args);
// delete options;
char *main_class_c = (*env)->GetStringUTFChars(env, mainClassStr, 0);
jclass mainClass = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, main_class_c);
jmethodID mainMethod = (*runtimeJNIEnvPtr)->GetStaticMethodID(runtimeJNIEnvPtr, mainClass, "main", "([Ljava/lang/String;)V");
// Need recreate jobjectArray to make JNIEnv is 'runtimeJNIEnvPtr'.
jobjectArray runtime_main_argv = convert_from_char_array(runtimeJNIEnvPtr, main_argv, main_argc);
(*runtimeJNIEnvPtr)->CallStaticVoidMethod(runtimeJNIEnvPtr, mainClass, mainMethod, runtime_main_argv);
(*env)->ReleaseStringUTFChars(env, mainClassStr, main_class_c);
free_char_array(env, mainArgArr, main_argv);
free_char_array(env, vmArgArr, vm_argv);
return res;
}
static jint launchJVM(int argc, char** argv) {
logArgs(argc, argv);
void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL);
// Boardwalk: silence
// LOGD("JLI lib = %x", (int)libjli);
if (NULL == libjli) {
LOGE("JLI lib = NULL: %s", dlerror());
return 0;
}
LOGD("Found JLI lib");
JLI_Launch_func *pJLI_Launch =
(JLI_Launch_func *)dlsym(libjli, "JLI_Launch");
// Boardwalk: silence
// LOGD("JLI_Launch = 0x%x", *(int*)&pJLI_Launch);
if (NULL == pJLI_Launch) {
LOGE("JLI_Launch = NULL");
return 0;
}
LOGD("Calling JLI_Launch");
return pJLI_Launch(argc, argv,
0, NULL, 0, NULL, FULL_VERSION,
DOT_VERSION, *argv, *argv, /* "java", "openjdk", */
JNI_FALSE, JNI_TRUE, JNI_FALSE, 0);
}
/*
* Class: com_oracle_embedded_launcher_VMLauncher
* Method: launchJVM
* Signature: ([Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_oracle_dalvik_VMLauncher_launchJVM(JNIEnv *env, jclass clazz, jobjectArray argsArray) {
jint res = 0;
// int i;
// Save dalvik JNIEnv pointer for JVM launch thread
dalvikJNIEnvPtr = env;
if (argsArray == NULL) {
LOGE("Args array null, returning");
//handle error
return 0;
}
int argc = (*env)->GetArrayLength(env, argsArray);
char **argv = convert_to_char_array(env, argsArray);
LOGD("Done processing args");
res = launchJVM(argc, argv);
LOGD("Freeing args");
free_char_array(env, argsArray, argv);
LOGD("Free done");
return res;
}

26
app/src/main/jni/log.h Normal file
View File

@ -0,0 +1,26 @@
#ifdef __ANDROID__
#include <android/log.h>
#define TAG "LaunchJVM"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __ANDROID__
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#else
#define LOGE(...) printf(__VA_ARGS__)
#define LOGW(...) printf(__VA_ARGS__)
#define LOGI(...) printf(__VA_ARGS__)
#define LOGD(...) printf(__VA_ARGS__)
#endif
#ifdef __cplusplus
}
#endif

162
app/src/main/jni/utils.c Normal file
View File

@ -0,0 +1,162 @@
#include <jni.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "log.h"
#include "utils.h"
typedef int (*Main_Function_t)(int, char**);
typedef void (*android_update_LD_LIBRARY_PATH_t)(char*);
long shared_awt_surface;
char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray) {
int num_rows = (*env)->GetArrayLength(env, jstringArray);
char **cArray = (char **) malloc(num_rows * sizeof(char*));
jstring row;
for (int i = 0; i < num_rows; i++) {
row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0);
}
return cArray;
}
jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows) {
jobjectArray resultArr = (*env)->NewObjectArray(env, num_rows, (*env)->FindClass(env, "java/lang/String"), NULL);
jstring row;
for (int i = 0; i < num_rows; i++) {
row = (jstring) (*env)->NewStringUTF(env, charArray[i]);
(*env)->SetObjectArrayElement(env, resultArr, i, row);
}
return resultArr;
}
void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray) {
int num_rows = (*env)->GetArrayLength(env, jstringArray);
jstring row;
for (int i = 0; i < num_rows; i++) {
row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
(*env)->ReleaseStringUTFChars(env, row, charArray[i]);
}
}
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setupBridgeSurfaceAWT(JNIEnv *env, jclass clazz, jlong surface) {
shared_awt_surface = surface;
}
JNIEXPORT jlong JNICALL Java_android_view_Surface_nativeGetBridgeSurfaceAWT(JNIEnv *env, jclass clazz) {
return (jlong) shared_awt_surface;
}
JNIEXPORT jint JNICALL Java_android_os_OpenJDKNativeRegister_nativeRegisterNatives(JNIEnv *env, jclass clazz, jstring registerSymbol) {
const char *register_symbol_c = (*env)->GetStringUTFChars(env, registerSymbol, 0);
void *symbol = dlsym(RTLD_DEFAULT, register_symbol_c);
if (symbol == NULL) {
printf("dlsym %s failed: %s\n", register_symbol_c, dlerror());
return -1;
}
int (*registerNativesForClass)(JNIEnv*) = symbol;
int result = registerNativesForClass(env);
(*env)->ReleaseStringUTFChars(env, registerSymbol, register_symbol_c);
return (jint) result;
}
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setLdLibraryPath(JNIEnv *env, jclass clazz, jstring ldLibraryPath) {
// jclass exception_cls = (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError");
android_update_LD_LIBRARY_PATH_t android_update_LD_LIBRARY_PATH;
void *libdl_handle = dlopen("libdl.so", RTLD_LAZY);
void *updateLdLibPath = dlsym(libdl_handle, "android_update_LD_LIBRARY_PATH");
if (updateLdLibPath == NULL) {
updateLdLibPath = dlsym(libdl_handle, "__loader_android_update_LD_LIBRARY_PATH");
if (updateLdLibPath == NULL) {
char *dl_error_c = dlerror();
LOGE("Error getting symbol android_update_LD_LIBRARY_PATH: %s", dl_error_c);
// (*env)->ThrowNew(env, exception_cls, dl_error_c);
}
}
android_update_LD_LIBRARY_PATH = (android_update_LD_LIBRARY_PATH_t) updateLdLibPath;
const char* ldLibPathUtf = (*env)->GetStringUTFChars(env, ldLibraryPath, 0);
android_update_LD_LIBRARY_PATH(ldLibPathUtf);
(*env)->ReleaseStringUTFChars(env, ldLibraryPath, ldLibPathUtf);
}
JNIEXPORT jboolean JNICALL Java_net_kdt_pojavlaunch_JREUtils_dlopen(JNIEnv *env, jclass clazz, jstring name) {
const char *nameUtf = (*env)->GetStringUTFChars(env, name, 0);
void* handle = dlopen(nameUtf, RTLD_GLOBAL | RTLD_LAZY);
if (!handle) {
LOGE("dlopen %s failed: %s", nameUtf, dlerror());
} else {
LOGD("dlopen %s success", nameUtf);
}
(*env)->ReleaseStringUTFChars(env, name, nameUtf);
return handle != NULL;
}
JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_chdir(JNIEnv *env, jclass clazz, jstring nameStr) {
const char *name = (*env)->GetStringUTFChars(env, nameStr, NULL);
int retval = chdir(name);
(*env)->ReleaseStringUTFChars(env, nameStr, name);
return retval;
}
JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_executeBinary(JNIEnv *env, jclass clazz, jobjectArray cmdArgs) {
jclass exception_cls = (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError");
jstring execFile = (*env)->GetObjectArrayElement(env, cmdArgs, 0);
char *exec_file_c = (char*) (*env)->GetStringUTFChars(env, execFile, 0);
void *exec_binary_handle = dlopen(exec_file_c, RTLD_LAZY);
// (*env)->ReleaseStringUTFChars(env, ldLibraryPath, ld_library_path_c);
(*env)->ReleaseStringUTFChars(env, execFile, exec_file_c);
char *exec_error_c = dlerror();
if (exec_error_c != NULL) {
LOGE("Error: %s", exec_error_c);
(*env)->ThrowNew(env, exception_cls, exec_error_c);
return -1;
}
Main_Function_t Main_Function;
Main_Function = (Main_Function_t) dlsym(exec_binary_handle, "main");
exec_error_c = dlerror();
if (exec_error_c != NULL) {
LOGE("Error: %s", exec_error_c);
(*env)->ThrowNew(env, exception_cls, exec_error_c);
return -1;
}
int cmd_argv = (*env)->GetArrayLength(env, cmdArgs);
char **cmd_args_c = convert_to_char_array(env, cmdArgs);
int result = Main_Function(cmd_argv, cmd_args_c);
free_char_array(env, cmdArgs, cmd_args_c);
return result;
}
// METHOD 2
/*
JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_executeForkedBinary(JNIEnv *env, jclass clazz, jobjectArray cmdArgs) {
int x, status;
x = fork();
if (x > 0) {
wait(&status);
} else {
execvpe();
}
return status;
}
*/

9
app/src/main/jni/utils.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _BINARY_UTILS_H_
#define _BINARY_UTILS_H_
char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray);
jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows);
void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray);
#endif // _BINARY_UTILS_H_

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TextureView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/installmod_surfaceview"/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/control_button"
android:layout_alignParentRight="true"
android:text="@string/control_forceclose"
android:onClick="forceClose"/>
</RelativeLayout>

View File

@ -48,7 +48,7 @@
<!-- AlertDialog title -->
<string name="alerttitle_selectkeymap">Pilih sebuah keymap json</string>
<string name="alerttitle_installmod">Pilih sebuah mod untuk di tambah</string>
<string name="alerttitle_installmod">Pilih sebuah mod untuk dipasang</string>
<string name="alerttitle_installoptifine">Pilih jar-nya OptiFine</string>
<!-- Error messages -->

View File

@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="mcl_options">
<item>@string/mcl_option_modmgr</item>
<!-- <item>@string/mcl_option_forgeinstall</item> -->
<item>@string/mcl_option_optifineinstall</item>
<item>@string/mcl_option_modinstall</item>
<item>@string/mcl_option_customcontrol</item>
<item>@string/mcl_option_settings</item>
<item>@string/mcl_option_about</item>

View File

@ -48,8 +48,7 @@
<!-- AlertDialog title -->
<string name="alerttitle_selectkeymap">Select a keymap json</string>
<string name="alerttitle_installmod">Select a mod to add</string>
<string name="alerttitle_installoptifine">Select OptiFine jar file</string>
<string name="alerttitle_installmod">Select a mod to install</string>
<!-- Error messages -->
<string name="error_fatal">PojavLauncher has unexpectedly crashed</string>
@ -87,7 +86,7 @@
<string name="mcl_options">Options</string>
<string name="mcl_option_modmgr">Mod manager (no function)</string>
<string name="mcl_option_optifineinstall">Install OptiFine</string>
<string name="mcl_option_modinstall">Install mod (Forge, LabyMod, Fabric,...)</string>
<string name="mcl_option_checkupdate">Check for update</string>
<string name="mcl_option_customcontrol">Custom controls</string>
<string name="mcl_option_settings">Settings</string>