Замер: для gm_construct реально читаются 335 файлов из VPK на 27,0 МБ (277 материалов, 23 модели, 35 Lua-скриптов; крупнейшее — m_anm.ani 6,8 МБ) плюс карта 35,0 МБ. Итого 62,1 МБ сырых, 31 МБ в tar.gz — против 6,7 ГБ полной установки. Этого набора достаточно: клиент запускается с ним, карта и пистолет полностью текстурированы (проверено кадром). - VirtualFs: запись успешных чтений (start_recording/take_recorded); - команда `gmod-rs fs pack <карта> --out <каталог>`: собирает мир, грузит модели игрока и оружия, автозагрузку Lua и копирует прочитанные файлы в структуру garrysmod/... плюс карту — получается самодостаточный корень ассетов для `--assets`; - android/build.sh: если есть android/assets/gmod (или GMOD_RS_ANDROID_ASSETS), каталог кладётся в APK как assets/gmod; - MainActivity: при первом запуске распаковывает assets/gmod в files/gmod_server/server, если карты там ещё нет — APK самодостаточен, adb push не нужен; - .gitignore: android/assets/ (ассеты GMod не коммитим). Все сьюты зелёные.
155 lines
5.9 KiB
Bash
Executable file
155 lines
5.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# Сборка debug-APK клиента gmod-rs под Android (arm64-v8a).
|
||
#
|
||
# Без Gradle: SDK/NDK + cargo + aapt2/d8/apksigner. Так проще в окружении без
|
||
# скачивания Android Gradle Plugin, а результатом всё равно является обычный
|
||
# подписанный APK.
|
||
set -euo pipefail
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
ANDROID_DIR="$ROOT/android"
|
||
BUILD="$ANDROID_DIR/build"
|
||
OUT_DIR="$ROOT/target/android"
|
||
API=31
|
||
ABI="${ANDROID_ABI:-arm64-v8a}"
|
||
case "$ABI" in
|
||
arm64-v8a) TARGET=aarch64-linux-android; CLANG_PREFIX=aarch64-linux-android ;;
|
||
x86_64) TARGET=x86_64-linux-android; CLANG_PREFIX=x86_64-linux-android ;;
|
||
armeabi-v7a) TARGET=armv7-linux-androideabi; CLANG_PREFIX=armv7a-linux-androideabi ;;
|
||
x86) TARGET=i686-linux-android; CLANG_PREFIX=i686-linux-android ;;
|
||
*) echo "неизвестный ABI: $ABI" >&2; exit 1 ;;
|
||
esac
|
||
if [[ "$ABI" == "arm64-v8a" ]]; then
|
||
OUT="$OUT_DIR/gmod-rs-debug.apk"
|
||
else
|
||
OUT="$OUT_DIR/gmod-rs-debug-$ABI.apk"
|
||
fi
|
||
|
||
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
|
||
if [[ -z "$SDK" || ! -d "$SDK" ]]; then
|
||
for candidate in "$HOME/Android/Sdk" /opt/android-sdk; do
|
||
if [[ -d "$candidate" ]]; then SDK="$candidate"; break; fi
|
||
done
|
||
fi
|
||
if [[ -z "$SDK" || ! -d "$SDK" ]]; then
|
||
echo "не найден Android SDK (задайте ANDROID_HOME)" >&2
|
||
exit 1
|
||
fi
|
||
|
||
NDK="${ANDROID_NDK_HOME:-${ANDROID_NDK:-}}"
|
||
if [[ -z "$NDK" || ! -d "$NDK" ]]; then
|
||
NDK="$(find "$SDK/ndk" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort -V | tail -1 || true)"
|
||
fi
|
||
if [[ -z "$NDK" || ! -d "$NDK" ]]; then
|
||
NDK=/opt/android-ndk
|
||
fi
|
||
if [[ ! -f "$NDK/build/cmake/android.toolchain.cmake" ]]; then
|
||
echo "не найден Android NDK (задайте ANDROID_NDK_HOME)" >&2
|
||
exit 1
|
||
fi
|
||
|
||
BUILD_TOOLS="$(find "$SDK/build-tools" -maxdepth 1 -mindepth 1 -type d | sort -V | tail -1)"
|
||
PLATFORM_DIR="$(find "$SDK/platforms" -maxdepth 1 -mindepth 1 -type d | sort -V | tail -1)"
|
||
ANDROID_JAR="$PLATFORM_DIR/android.jar"
|
||
if [[ ! -f "$ANDROID_JAR" ]]; then
|
||
echo "не найден android.jar в $PLATFORM_DIR" >&2
|
||
exit 1
|
||
fi
|
||
|
||
TOOLCHAIN="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||
CC_BIN="$TOOLCHAIN/${CLANG_PREFIX}${API}-clang"
|
||
if [[ ! -x "$CC_BIN" ]]; then
|
||
CC_BIN="$TOOLCHAIN/${CLANG_PREFIX}-clang"
|
||
fi
|
||
if [[ ! -x "$CC_BIN" ]]; then
|
||
echo "не найден clang NDK для $TARGET в $TOOLCHAIN" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# --- нативная часть -------------------------------------------------------
|
||
export ANDROID_NDK="$NDK"
|
||
export CMAKE_TOOLCHAIN_FILE="$NDK/build/cmake/android.toolchain.cmake"
|
||
export ANDROID_PLATFORM="android-$API"
|
||
TARGET_U="${TARGET//-/_}"
|
||
export "CC_${TARGET_U}=$CC_BIN"
|
||
export "AR_${TARGET_U}=$TOOLCHAIN/llvm-ar"
|
||
export "RANLIB_${TARGET_U}=$TOOLCHAIN/llvm-ranlib"
|
||
LINKER_VAR="CARGO_TARGET_$(echo "$TARGET_U" | tr '[:lower:]' '[:upper:]')_LINKER"
|
||
export "$LINKER_VAR=$CC_BIN"
|
||
|
||
echo "== cargo build ($TARGET / $ABI, release)"
|
||
(cd "$ROOT" && cargo build --release -p client --lib --target "$TARGET")
|
||
|
||
LIB_DIR="$ROOT/target/$TARGET/release"
|
||
SDL_SO="$LIB_DIR/libSDL3.so"
|
||
CLIENT_SO="$LIB_DIR/libclient.so"
|
||
if [[ ! -f "$SDL_SO" ]]; then
|
||
# sdl3-sys кладёт собранную SDL3 в каталог сборки, если не смог
|
||
# скопировать её наверх.
|
||
SDL_SO="$(find "$ROOT/target/$TARGET/release/build" -path '*/out/lib/libSDL3.so' | head -1)"
|
||
fi
|
||
if [[ ! -f "$SDL_SO" || ! -f "$CLIENT_SO" ]]; then
|
||
echo "не найдены собранные libSDL3.so/libclient.so" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# --- Java + пакет ---------------------------------------------------------
|
||
rm -rf "$BUILD"
|
||
mkdir -p "$BUILD/classes" "$BUILD/dex" "$BUILD/lib/$ABI" "$OUT_DIR"
|
||
|
||
echo "== javac"
|
||
find "$ANDROID_DIR/app/src/main/java" -name '*.java' > "$BUILD/sources.txt"
|
||
javac --release 11 -Xlint:-options -cp "$ANDROID_JAR" -d "$BUILD/classes" @"$BUILD/sources.txt"
|
||
|
||
echo "== d8"
|
||
find "$BUILD/classes" -name '*.class' > "$BUILD/classes.txt"
|
||
"$BUILD_TOOLS/d8" --release --lib "$ANDROID_JAR" --min-api "$API" \
|
||
--output "$BUILD/dex" @"$BUILD/classes.txt"
|
||
|
||
echo "== aapt2 link"
|
||
"$BUILD_TOOLS/aapt2" link -o "$BUILD/unsigned.apk" \
|
||
--manifest "$ANDROID_DIR/AndroidManifest.xml" \
|
||
-I "$ANDROID_JAR"
|
||
|
||
cp "$SDL_SO" "$BUILD/lib/$ABI/libSDL3.so"
|
||
# SDLActivity грузит «main»: имя файла обязано быть libmain.so, а не
|
||
# libclient.so (внутри — наш cdylib с экспортом SDL_main).
|
||
cp "$CLIENT_SO" "$BUILD/lib/$ABI/libmain.so"
|
||
"$TOOLCHAIN/llvm-strip" --strip-debug "$BUILD/lib/$ABI/libSDL3.so" 2>/dev/null || true
|
||
"$TOOLCHAIN/llvm-strip" --strip-debug "$BUILD/lib/$ABI/libmain.so" 2>/dev/null || true
|
||
|
||
(cd "$BUILD/dex" && zip -q "$BUILD/unsigned.apk" classes.dex)
|
||
(cd "$BUILD" && zip -q -r "$BUILD/unsigned.apk" "lib")
|
||
|
||
# Встроенные ассеты (необязательно): `fs pack` кладёт сюда garrysmod/.
|
||
ASSETS_DIR="${GMOD_RS_ANDROID_ASSETS:-$ANDROID_DIR/assets}"
|
||
if [[ -d "$ASSETS_DIR/gmod" ]]; then
|
||
echo "== assets: $(du -sh "$ASSETS_DIR/gmod" | cut -f1)"
|
||
rm -rf "$BUILD/assets"
|
||
mkdir -p "$BUILD/assets"
|
||
cp -a "$ASSETS_DIR/gmod" "$BUILD/assets/gmod"
|
||
(cd "$BUILD" && zip -q -r "$BUILD/unsigned.apk" "assets")
|
||
fi
|
||
|
||
echo "== zipalign"
|
||
"$BUILD_TOOLS/zipalign" -f 4 "$BUILD/unsigned.apk" "$BUILD/aligned.apk"
|
||
|
||
KEYSTORE="$ANDROID_DIR/debug.keystore"
|
||
if [[ ! -f "$KEYSTORE" ]]; then
|
||
echo "== debug keystore"
|
||
keytool -genkeypair -v -keystore "$KEYSTORE" \
|
||
-storepass android -keypass android \
|
||
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
|
||
-dname "CN=Android Debug,O=Android,C=US" >/dev/null
|
||
fi
|
||
|
||
echo "== apksigner"
|
||
"$BUILD_TOOLS/apksigner" sign \
|
||
--ks "$KEYSTORE" --ks-pass pass:android --key-pass pass:android \
|
||
--ks-key-alias androiddebugkey \
|
||
--out "$OUT" "$BUILD/aligned.apk"
|
||
"$BUILD_TOOLS/apksigner" verify --verbose "$OUT"
|
||
|
||
echo
|
||
echo "APK: $OUT"
|
||
echo "install: adb install -r \"$OUT\""
|