android-native: SDL3 APK build for the native client
build-android-native.sh downloads SDL 3.4.16 (sha256-checked), builds libSDL3.so and libmain.so (native_render + port_platform + embedded Python) at API 24, and wraps them in the SDLActivity shell under android-native/ (package org.metin2port.client). push-android-client.sh copies the 40250 Client directory into the app's files/ with run-as (--locale XX selects locale_XX.cfg). port_logic links with --no-dependent-libraries on ELF targets so the verbatim `#pragma comment(lib, ...)` lines do not break lld. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0b701d88c3
commit
fd051cc3ed
@@ -78,3 +78,10 @@ __pycache__/
|
|||||||
|
|
||||||
# Written by 40250 uitaskbar.py (SaveMouseButtonSettings) into the working directory at runtime.
|
# Written by 40250 uitaskbar.py (SaveMouseButtonSettings) into the working directory at runtime.
|
||||||
/project/mouse.cfg
|
/project/mouse.cfg
|
||||||
|
|
||||||
|
# native Android client (./build-android-native.sh): SDL3 download, NDK builds, Gradle output
|
||||||
|
/build-android-native/
|
||||||
|
/android-native/.gradle/
|
||||||
|
/android-native/app/build/
|
||||||
|
/android-native/build/
|
||||||
|
/android-native/local.properties
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// SDLActivity shell around native_render (libmain.so). The native libraries, the SDL Java sources
|
||||||
|
// and the assets come from ../build-android-native, produced by ../build-android-native.sh.
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
}
|
||||||
|
|
||||||
|
def nativeBuild = file("${rootDir}/../build-android-native")
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = 'org.metin2port.client'
|
||||||
|
compileSdk 35
|
||||||
|
defaultConfig {
|
||||||
|
applicationId 'org.metin2port.client'
|
||||||
|
minSdk 24
|
||||||
|
targetSdk 35
|
||||||
|
versionCode 1
|
||||||
|
versionName '0.1'
|
||||||
|
ndk { abiFilters 'arm64-v8a' }
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceSets.main {
|
||||||
|
java.srcDirs += "${nativeBuild}/_deps/SDL3-3.4.16/android-project/app/src/main/java"
|
||||||
|
jniLibs.srcDirs = ["${nativeBuild}/jniLibs"]
|
||||||
|
assets.srcDirs = ["${nativeBuild}/app-build/native_render/android-assets"]
|
||||||
|
}
|
||||||
|
packagingOptions {
|
||||||
|
jniLibs { useLegacyPackaging = true }
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
lint { abortOnError = false }
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x400003" android:required="true" />
|
||||||
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
|
<application android:label="@string/app_name"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:extractNativeLibs="true"
|
||||||
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
|
<activity android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:alwaysRetainTaskState="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:screenOrientation="sensorLandscape"
|
||||||
|
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||||
|
android:preferMinimalPostProcessing="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.metin2port.client;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import org.libsdl.app.SDLActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs native_render's main() (libmain.so). Command-line arguments come from the launch intent,
|
||||||
|
* e.g. adb shell am start -n org.metin2port.client/.MainActivity \
|
||||||
|
* --es args "--live-server 192.168.21.203:11000:13000 --login-screen"
|
||||||
|
* Without arguments the client runs against the in-process fake server once Client/ is in files/.
|
||||||
|
*/
|
||||||
|
public class MainActivity extends SDLActivity {
|
||||||
|
@Override
|
||||||
|
protected String[] getLibraries() {
|
||||||
|
return new String[] { "SDL3", "main" };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getArguments() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String args = intent != null ? intent.getStringExtra("args") : null;
|
||||||
|
if (args == null || args.trim().isEmpty())
|
||||||
|
return new String[0];
|
||||||
|
return args.trim().split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Metin2</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.android.application' version '8.7.3' apply false
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx2048m
|
||||||
|
android.useAndroidX=false
|
||||||
Binary file not shown.
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name = 'metin2-native'
|
||||||
|
include ':app'
|
||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the native (SDL3 + Vulkan) 40250 client as an Android arm64 APK.
|
||||||
|
# ./build-android-native.sh [Debug|Release] [--install]
|
||||||
|
# Output: android-native/app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
# The 1.4 GB Client/ directory is not in the APK; push it once with ./push-android-client.sh.
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
REPO="$PWD"
|
||||||
|
|
||||||
|
CONFIG=Release
|
||||||
|
INSTALL=0
|
||||||
|
for a in "$@"; do
|
||||||
|
case "$a" in
|
||||||
|
Debug|Release) CONFIG="$a" ;;
|
||||||
|
--install) INSTALL=1 ;;
|
||||||
|
*) echo "usage: $0 [Debug|Release] [--install]" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
API=24
|
||||||
|
OUT="$REPO/build-android-native"
|
||||||
|
JOBS="${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}"
|
||||||
|
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Library/Android/sdk}}"
|
||||||
|
|
||||||
|
# --- NDK: $ANDROID_NDK_HOME, else the installed 27.x, else the newest ---
|
||||||
|
NDK="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-}}"
|
||||||
|
if [ -z "$NDK" ] && [ -d "$SDK/ndk" ]; then
|
||||||
|
v=$(ls -1 "$SDK/ndk" | grep '^27\.' | sort -V | tail -1 || true)
|
||||||
|
[ -n "$v" ] || v=$(ls -1 "$SDK/ndk" | sort -V | tail -1)
|
||||||
|
NDK="$SDK/ndk/$v"
|
||||||
|
fi
|
||||||
|
[ -f "$NDK/build/cmake/android.toolchain.cmake" ] || { echo "Android NDK not found (set ANDROID_NDK_HOME)" >&2; exit 1; }
|
||||||
|
echo "NDK: $NDK"
|
||||||
|
TOOLCHAIN="$NDK/build/cmake/android.toolchain.cmake"
|
||||||
|
GLSLC="${GLSLC:-$(command -v glslc || true)}"
|
||||||
|
[ -n "$GLSLC" ] || { echo "glslc not found (brew install shaderc)" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- SDL3 source (Java SDLActivity + libSDL3.so), same release as the macOS build ---
|
||||||
|
SDL_VER=3.4.16
|
||||||
|
SDL_SHA256=7322236cd12090c3eb40b9728be4d49c76f66ad17d04369584d4ecad5cf77c68
|
||||||
|
SDL_SRC="$OUT/_deps/SDL3-$SDL_VER"
|
||||||
|
if [ ! -f "$SDL_SRC/CMakeLists.txt" ]; then
|
||||||
|
mkdir -p "$OUT/_deps"
|
||||||
|
tarball="$OUT/_deps/SDL3-$SDL_VER.tar.gz"
|
||||||
|
curl -fL -o "$tarball" "https://github.com/libsdl-org/SDL/releases/download/release-$SDL_VER/SDL3-$SDL_VER.tar.gz"
|
||||||
|
echo "$SDL_SHA256 $tarball" | shasum -a 256 -c -
|
||||||
|
tar xzf "$tarball" -C "$OUT/_deps"
|
||||||
|
fi
|
||||||
|
if [ ! -f "$OUT/sdl3/lib/libSDL3.so" ]; then
|
||||||
|
cmake -S "$SDL_SRC" -B "$OUT/sdl3-build" -G "Unix Makefiles" \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM="android-$API" \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release -DSDL_SHARED=ON -DSDL_STATIC=OFF -DSDL_TEST_LIBRARY=OFF \
|
||||||
|
-DSDL_TESTS=OFF -DSDL_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX="$OUT/sdl3"
|
||||||
|
cmake --build "$OUT/sdl3-build" -j "$JOBS"
|
||||||
|
cmake --install "$OUT/sdl3-build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- libmain.so (native_render + port_platform + embedded Python) and its assets ---
|
||||||
|
cmake -S . -B "$OUT/app-build" -G "Unix Makefiles" -Wno-deprecated \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM="android-$API" \
|
||||||
|
-DCMAKE_BUILD_TYPE="$CONFIG" -DMTGODOT_BUILD_TOOLS=OFF -DBUILD_TESTING=OFF \
|
||||||
|
-DMT_BUILD_NATIVE_RENDER_PROTOTYPE=ON -DSDL3_DIR="$OUT/sdl3/lib/cmake/SDL3" \
|
||||||
|
-DCMAKE_FIND_ROOT_PATH="$OUT/sdl3" -DGLSLC="$GLSLC" >/dev/null
|
||||||
|
cmake --build "$OUT/app-build" --target main mt_native_android_assets -j "$JOBS"
|
||||||
|
|
||||||
|
mkdir -p "$OUT/jniLibs/arm64-v8a"
|
||||||
|
cp -f "$OUT/sdl3/lib/libSDL3.so" "$OUT/app-build/native_render/libmain.so" "$OUT/jniLibs/arm64-v8a/"
|
||||||
|
STRIP=$(ls -1d "$NDK"/toolchains/llvm/prebuilt/*/bin/llvm-strip | head -1)
|
||||||
|
"$STRIP" --strip-unneeded "$OUT/jniLibs/arm64-v8a/libSDL3.so" "$OUT/jniLibs/arm64-v8a/libmain.so"
|
||||||
|
|
||||||
|
# --- APK ---
|
||||||
|
if [ -z "${JAVA_HOME:-}" ]; then
|
||||||
|
for j in /opt/homebrew/opt/openjdk@21 /opt/homebrew/opt/openjdk@17; do
|
||||||
|
[ -x "$j/bin/java" ] && { export JAVA_HOME="$j"; break; }
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
export ANDROID_HOME="$SDK"
|
||||||
|
variant=Debug # debug-signed either way (sideload); CONFIG picks the native optimisation level
|
||||||
|
(cd android-native && ./gradlew --quiet "assemble$variant")
|
||||||
|
APK="$REPO/android-native/app/build/outputs/apk/debug/app-debug.apk"
|
||||||
|
ls -la "$APK"
|
||||||
|
|
||||||
|
if [ "$INSTALL" = 1 ]; then
|
||||||
|
adb install -r "$APK"
|
||||||
|
fi
|
||||||
@@ -47,6 +47,11 @@ if(NOT MSVC)
|
|||||||
set(MT_PORT_WARNING_FLAGS -Wno-unknown-pragmas -Wno-format-security)
|
set(MT_PORT_WARNING_FLAGS -Wno-unknown-pragmas -Wno-format-security)
|
||||||
endif()
|
endif()
|
||||||
target_compile_options(port_logic PUBLIC ${MT_PORT_WARNING_FLAGS})
|
target_compile_options(port_logic PUBLIC ${MT_PORT_WARNING_FLAGS})
|
||||||
|
# 40250 headers keep `#pragma comment(lib, "d3d8.lib")` etc. verbatim; clang turns them into ELF
|
||||||
|
# .deplibs that lld then fails to resolve (ld64 on Apple ignores unknown ones).
|
||||||
|
if(CMAKE_SYSTEM_NAME MATCHES "Android|Linux")
|
||||||
|
target_link_options(port_logic INTERFACE LINKER:--no-dependent-libraries)
|
||||||
|
endif()
|
||||||
target_compile_features(port_logic PUBLIC cxx_std_20)
|
target_compile_features(port_logic PUBLIC cxx_std_20)
|
||||||
# Armadillo nanomite markers (StdAfx.h `__asm _emit` jumps over a signature): x86-32 MSVC inline asm that no
|
# Armadillo nanomite markers (StdAfx.h `__asm _emit` jumps over a signature): x86-32 MSVC inline asm that no
|
||||||
# target here accepts, and a no-op without the Armadillo wrapper, so they expand to nothing.
|
# target here accepts, and a no-op without the Armadillo wrapper, so they expand to nothing.
|
||||||
|
|||||||
+21
-14
@@ -32,21 +32,28 @@ returned by SDL's `SDL_GetBasePath`). `MT_PYTHON_STDLIB` can override the zip pa
|
|||||||
|
|
||||||
## Android integration
|
## Android integration
|
||||||
|
|
||||||
With an Android toolchain and SDL3 Android AAR/Prefab available to CMake, the
|
`./build-android-native.sh [Debug|Release] [--install]` builds the arm64 APK
|
||||||
same option builds `libmain.so` for SDLActivity. The `mt_native_android_assets`
|
(`android-native/app/build/outputs/apk/debug/app-debug.apk`, package
|
||||||
target prepares `native.vert.spv`, `native.frag.spv`, and `python27.zip` under
|
`org.metin2port.client`): it downloads SDL 3.4.16 (sha256-checked) into
|
||||||
`<build>/native_render/android-assets` (`python27.zip` is included when the
|
`build-android-native/_deps`, builds `libSDL3.so`, cross-builds `libmain.so`
|
||||||
embedded Python target is enabled); include that directory in the SDL app's
|
(this target + `port_platform` + embedded Python) at API 24, and wraps both in the
|
||||||
`assets.srcDirs`. The app must allow network access for login. The 40250 `Client`
|
SDLActivity shell under `android-native/` (`MainActivity` passes the launch intent's
|
||||||
directory must be placed at `SDL_GetPrefPath("mtgodot", "native-render")/Client`,
|
`args` extra as argv). `mt_native_android_assets` supplies `native.vert.spv`,
|
||||||
with `pack/Index` present, before live mode starts. The Godot APK is a separate
|
`native.frag.spv` and `python27.zip` as APK assets.
|
||||||
application and does not launch this SDL renderer.
|
|
||||||
|
|
||||||
The standalone `arm64-v8a` renderer cross-builds at Android API 24. The full
|
The 40250 `Client` directory is not packaged. `./push-android-client.sh` copies it
|
||||||
live-client cross-build currently stops in `extension/src/port/common/Win32Crt.cpp`:
|
to the app's `files/Client` (`SDL_GetPrefPath`) with `run-as`, so it needs the
|
||||||
the NDK exposes `<iconv.h>` at API 24 but does not declare `iconv` until API 28.
|
debug-signed APK installed first. Without arguments the app runs against the
|
||||||
This is a port-runtime prerequisite for an Android live-client APK; raising the
|
in-process fake server; for a real server:
|
||||||
minimum API level is not assumed here.
|
|
||||||
|
```sh
|
||||||
|
adb shell am start -n org.metin2port.client/.MainActivity \
|
||||||
|
--es args "--live-server HOST:AUTH_PORT:GAME_PORT --login-screen"
|
||||||
|
```
|
||||||
|
|
||||||
|
Code page conversion does not use iconv: `extension/src/codepage` carries Microsoft's
|
||||||
|
WindowsBestFit tables for 874/932/936/949/950/1250-1258 (regenerate with
|
||||||
|
`python3 script/gen_codepage_tables.py`), so Android converts the same as macOS.
|
||||||
|
|
||||||
The Vulkan portability enumeration extension is selected only when advertised
|
The Vulkan portability enumeration extension is selected only when advertised
|
||||||
by the loader. DDS, TGA, and memory textures retain their existing decoders;
|
by the loader. DDS, TGA, and memory textures retain their existing decoders;
|
||||||
|
|||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copy the 40250 Client/ directory into the Android app's private files/ (SDL_GetPrefPath), where
|
||||||
|
# native_render looks for files/Client/pack/Index. Needs the debug APK installed (run-as).
|
||||||
|
# ./push-android-client.sh [--locale XX] [CLIENT_DIR]
|
||||||
|
# --locale XX writes the pushed locale.cfg from CLIENT_DIR/locale_XX.cfg. --locale zh defaults
|
||||||
|
# CLIENT_DIR to build/zh-locale/Client (script/build_zh_locale.py), whose pack/ registers locale_zh.
|
||||||
|
# Symbolic links in CLIENT_DIR (the zh client links the original packs) are copied as their targets.
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
PKG=org.metin2port.client
|
||||||
|
LOCALE=""
|
||||||
|
if [ "${1:-}" = "--locale" ]; then
|
||||||
|
LOCALE="${2:?--locale needs a locale code}"
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
if [ "$LOCALE" = zh ]; then
|
||||||
|
CLIENT="${1:-build/zh-locale/Client}"
|
||||||
|
else
|
||||||
|
CLIENT="${1:-Client}"
|
||||||
|
fi
|
||||||
|
[ -f "$CLIENT/pack/Index" ] || { echo "$CLIENT/pack/Index not found" >&2; exit 1; }
|
||||||
|
# The archive's top directory is basename(CLIENT DIR); the app reads files/Client.
|
||||||
|
[ "$(basename "$CLIENT")" = Client ] || { echo "CLIENT_DIR must be named Client" >&2; exit 1; }
|
||||||
|
if [ -n "$LOCALE" ]; then
|
||||||
|
[ -f "$CLIENT/locale_$LOCALE.cfg" ] || { echo "$CLIENT/locale_$LOCALE.cfg not found" >&2; exit 1; }
|
||||||
|
pack="$(cut -d' ' -f3 < "$CLIENT/locale_$LOCALE.cfg" | tr -d '\r\n')"
|
||||||
|
tr -d '\r' < "$CLIENT/pack/Index" | grep -qxF "locale_$pack" ||
|
||||||
|
{ echo "locale_$pack is not registered in $CLIENT/pack/Index" >&2; exit 1; }
|
||||||
|
fi
|
||||||
|
TMP=/data/local/tmp/mt-client.tar
|
||||||
|
echo "packing $CLIENT ..."
|
||||||
|
tar -h -C "$(dirname "$CLIENT")" -cf - "$(basename "$CLIENT")" | adb shell "cat > $TMP"
|
||||||
|
echo "unpacking into $PKG files/ ..."
|
||||||
|
adb shell "run-as $PKG sh -c 'mkdir -p files && rm -rf files/Client && cd files && tar -xf $TMP' && rm -f $TMP"
|
||||||
|
if [ -n "$LOCALE" ]; then
|
||||||
|
adb shell "run-as $PKG sh -c 'cp files/Client/locale_$LOCALE.cfg files/Client/locale.cfg'"
|
||||||
|
echo "locale.cfg: $(adb shell "run-as $PKG cat files/Client/locale.cfg")"
|
||||||
|
fi
|
||||||
|
adb shell "run-as $PKG ls files/Client/pack/Index" >/dev/null && echo "done: files/Client"
|
||||||
Reference in New Issue
Block a user