Skip to content

Latest commit

 

History

History
158 lines (119 loc) · 14.8 KB

File metadata and controls

158 lines (119 loc) · 14.8 KB

Android Debugging

Engine-owned documentation. Paths under ../ are relative to the FOnline engine root. Paths under ../../ point to an embedding game project such as Last Frontier when this engine is used as a submodule.

Practical reference for building, packaging, installing, and remote-scene debugging the Android client.

Workflow Overview

Android debug work is split into four layers:

  1. prepare the Android SDK/NDK workspace parts;
  2. build the Android client native library;
  3. package a Gradle project with baked resources;
  4. build/install/launch the APK on a Wi-Fi ADB device.

The current supported platform identifiers are android-arm32, android-arm64, and android-x86. Most local tasks use android-arm64. A package emits an installable Android APK when its DefinePackage(...) entry carries a BINARY Client Android arm64 Apk line — the raw LocalTest AndroidTest package is the reusable example. Which of an embedding project's release packages include Android (and which CI job builds them) is that project's own choice; consult the project's build/CI documentation for its package composition.

The high-level command flow from the repo root is:

bash ../BuildTools/prepare-workspace.sh android-packages android-arm64 # fresh Linux host
# or, when system packages are already present:
bash ../BuildTools/prepare-workspace.sh android-arm64
python3 ../BuildTools/buildtools.py build android-arm64 client RelWithDebInfo
python3 ../BuildTools/buildtools.py package-android-debug LF android-arm64 LocalTest
python3 ../BuildTools/android_device.py --workspace-root Workspace connect
cd Workspace/android-debug/LF-Client-LocalTest-Android
./gradlew assembleDebug
python3 ../BuildTools/android_device.py --workspace-root Workspace install --apk Workspace/android-debug/LF-Client-LocalTest-Android/app/build/outputs/apk/debug/app-debug.apk
python3 ../BuildTools/android_device.py --workspace-root Workspace launch --activity com.lastfrontier.app/.FOnlineActivity
python3 ../BuildTools/android_device.py --workspace-root Workspace logcat

VS Code Task Flow

The active Linux VS Code Android launch tasks are thin wrappers around the same BuildTools entry points:

  • Android :: Prepare Workspace
  • Android :: Build Client
  • Android :: Build Debug Package
  • Android :: Build APK
  • Android :: Connect Device
  • Android :: Install APK
  • Android :: Launch App
  • Android :: Logs App

Android :: Prepare Launch [linux] runs the standard sequence for a LocalTest APK.

Android :: Launch Remote Scene [linux] runs the remote-scene sequence: select scene, prepare workspace, bake resources, build LF_ServerHeadless, build/package/install RemoteSceneLaunch, stop older scene servers on the gameplay ports, start a fresh headless scene server, stop the app, and launch it with a server-host override.

Remote Scene Launch

Android scene debugging uses the RemoteSceneLaunch subconfig rather than the embedded-client SceneLaunch path.

Important differences:

  • the server is LF_ServerHeadless on the host;
  • the Android client is a separate device process;
  • Server.AutoStartClientOnServer stays disabled;
  • android_device.py launch-game passes ClientNetwork.ServerHost as an Android activity extra;
  • if --server-host is omitted, android_device.py auto-detects the host LAN IP by checking the route to the selected Wi-Fi device.

Use Android :: Launch Remote Scene [linux] after compatibility-affecting changes. Launching only the already installed APK can leave an older client talking to a newer scene server and produce Client outdated style failures.

Official Package Targets

The local debug flow above uses package-android-debug and then Gradle assembleDebug from Workspace/android-debug/.... The CI/release package flow is different: MakePackage-<type> targets are generated from the DefinePackage(...) entries in the embedding project's CMakeLists.txt. Any package whose entry lists BINARY Client Android arm64 Apk produces an installable APK alongside its other artifacts, provided the packaging job runs prepare-workspace android-sdk android-ndk first; the generated package lands under Workspace/output/<DevName>-<type> with the APK produced through the shared ../BuildTools/package.py Android packager. The official packager invokes Gradle with --no-daemon so concurrent package jobs on the same self-hosted runner do not reuse or kill each other's Gradle daemon.

Source paths inspected

  • ../BuildTools/buildtools.py
  • ../BuildTools/android_device.py
  • ../BuildTools/package.py
  • ../BuildTools/android-project/
  • ../BuildTools/android-project/app/src/main/java-template/FOnlineActivity.java
  • ../ThirdParty/android-sdk
  • ../ThirdParty/android-ndk
  • ../../.vscode/tasks.json
  • ../../CMakeLists.txt
  • ../../.github/workflows/ci.yml
  • ../../LastFrontier.fomain

Use this split when debugging Android output:

  • LocalTest/RemoteSceneLaunch device debugging -> inspect package-android-debug, Workspace/android-debug/LF-Client-*-Android, Wi-Fi ADB, and the VS Code Android tasks.
  • Release-package APK artifact issue -> inspect the embedding project's DefinePackage(...) entries, its CI package matrix, MakePackage-<type>, and Workspace/output/<DevName>-<type> rather than the local debug Gradle directory first.
  • AndroidTest package issue -> remember it is defined as BINARY Client Android arm64 Raw, not an APK-producing package target.

Runtime Resource Access

Packaging places complete .fores bases under app/src/main/assets/ using the configured client resource directory (Resources by default) and marks fores as noCompress. FOnlineActivity passes that directory inside <APK sourceDir>!/assets/ as Baking.ClientResources, the app files directory as Common.UserWritablePath, and its Cache directory as Baking.CacheResources. It does not copy or delete the resource tree on startup or package updates.

The engine locates each .fores entry stored in the APK ZIP and reads its bounded file region with 64-bit positional reads. A compressed/encrypted outer entry is not seekable through this route and is rejected. Updates append to <files>/Resources/Pack.patch.fores; a full refresh installs <files>/Resources/Pack.fores, which takes precedence over the APK base. Patch binding excludes an old patch when the selected physical base changes. See ResourcePackFormat.md and ClientUpdater.md.

Practical Debugging Notes

When Android launch behavior fails, isolate the failing layer before rebuilding the whole stack:

  • workspace prepare fails before build -> check ../ThirdParty/android-sdk, ../ThirdParty/android-ndk, and whether Workspace/android-sdk / Workspace/android-ndk were prepared by prepare-workspace.sh. Truncated Google CDN zips (ContentTooShortError, Error reading Zip content from a SeekableByteChannel) are retried by download_file / run_with_retry in ../BuildTools/buildtools.py; a persistent failure is a host/network problem, not a missing pin.
  • Gradle project exists but APK build fails -> inspect Workspace/android-debug/LF-Client-*-Android/local.properties; the VS Code tasks fall back to Workspace/android-sdk and then /usr/lib/android-sdk if needed.
  • APK install is canceled on device -> rerun android_device.py install after approving Android wireless debugging or unknown-app-install prompts on the device.
  • device is not found -> run android_device.py discover / connect; the helper uses adb mdns services, caches Workspace/android-debug/device-endpoint.txt, and falls back to manual IP[:port] input.
  • RemoteSceneLaunch app cannot connect back to host -> check the launch-game ClientNetwork.ServerHost override, the selected Wi-Fi route, and whether host ports 4025/4026 are already occupied by a stale server.
  • scene launch opens the wrong scene -> inspect the selected startupSceneName, LF_ServerHeadless --ApplySubConfig RemoteSceneLaunch --Scene.Startup <SceneId>, and the installed package path.
  • resources are stale after reinstall -> verify the APK was rebuilt from the expected Workspace/android-debug/LF-Client-*-Android directory, that its .fores assets are stored with noCompress, and that FOnlineActivity forwards the configured APK asset path. Check which base the runtime selects and whether app-private Resources contains a replacement base or patch overriding the APK.
  • packaging fails on icon, signing, or manifest metadata -> inspect Android.Icon, Android.Keystore, Android.KeystorePassword, Android.KeyAlias, Android.KeyPassword, and any Android.ManifestMetaData.* keys in ../../LastFrontier.fomain; icon input must be a PNG, manifest metadata values must be non-empty, and partial signing config is invalid.
  • Gradle cannot resolve an Android SDK dependency -> inspect the selected package config's Android.GradleMavenRepository.* and Android.GradleDependency.* settings in ../../LastFrontier.fomain; the packager copies those entries into the generated Gradle project only for configs that define them.
  • package-specific Java bridge is missing or in the wrong package -> inspect Android.JavaSource.* settings in ../../LastFrontier.fomain; non-empty entries are copied into the generated app package namespace and get $PACKAGE$ / $CONFIG$ placeholders patched by package.py.
  • ./gradlew answers Permission denied -> BuildTools/android-project/gradlew is committed with its executable bit (mode 100755), and the debug packager copies it with that mode; a checkout that lost the bit needs git update-index --chmod=+x rather than a local chmod each time.
  • the app aborts in System.loadLibrary with JNI DETECTED ERROR ... NoSuchMethodError ... SDLActivity.nativeSetupJNI -> the SDL Java glue in BuildTools/android-project/app/src/main/java/org/libsdl/app/ does not match the SDL the engine links: SDL registers its JNI methods by exact signature. Copy the glue from ../ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/ whenever SDL is updated and re-add the class-level @SuppressWarnings("deprecation"); BuildTools/tests/test_android_sdl_java_glue.py fails while the two differ. The glue was left behind when SDL changed nativeSetupJNI to return void (update of 2026-04-28), and every Android client aborted at startup from then on.
  • the engine aborts at startup with Executable path could not be resolved -> an Android or iOS app has no executable path; the client host resolves a bundled runtime beside the executable only where native modules can load (CanSelfUpdateNativeModules), so this message on a mobile target means that guard was bypassed.
  • the engine log is not in logcat -> the client writes files/LF.log in its own data directory; with a debug APK read it through adb shell run-as <package> cat files/LF.log (the debug package id is com.fonline.app).
  • the client dies in the renderer on an emulator -> the Vulkan backend requires a VK_FORMAT_B8G8R8A8_UNORM swapchain, which the emulator's SwiftShader surface does not offer, and the emulator's GLES answered GL_INVALID_ENUM (RenderingException: OpenGL error 1280) during the updater screen. Both are open renderer findings (Android devices commonly expose RGBA8 swapchains). A check that does not need drawing - such as ManagedScript.InteropProbeOnStart - runs with Render.NullRenderer=True in the packaged config.
  • CI package contains Windows/Web/Linux artifacts but no Android APK -> only package definitions whose DefinePackage(...) lists BINARY Client Android arm64 Apk emit APKs, and the CI job that builds such a package must prepare the android-sdk/android-ndk workspace; check the embedding project's package definitions and CI matrix.

Key Files and Integration Points

If you need to trace the Android debug flow through the live repository, start with these files:

  • README.md - concise repo-front-door Android command flow that this detailed device/debug guide expands
  • the embedding project's CMakeLists.txt DefinePackage(...) entries, including which package targets emit Android arm64 Apk artifacts
  • the embedding project's CI config - package matrix and the Android workspace-preparation step for APK-producing packages
  • ../../.vscode/tasks.json - live Android task graph for workspace prep, package build, install, remote-scene server startup, and app launch
  • ../BuildTools/buildtools.py - Android platform identifiers, workspace feature mapping, and package-android-debug entry point
  • ../BuildTools/android_device.py - Wi-Fi ADB discovery, connection caching, install, launch, launch-game, stop, and logcat helper
  • ../BuildTools/package.py - Android Gradle project generation, resource movement, icon/signing config, and APK build integration used by both debug and package targets
  • ../BuildTools/android-project/ - Gradle and activity template patched by the packager
  • ../BuildTools/android-project/app/src/main/java-template/FOnlineActivity.java - APK asset-path, writable-root and ClientNetwork.ServerHost argument forwarding
  • ../../LastFrontier.fomain - Android.*, LocalTest, and RemoteSceneLaunch config values used by packaging and launch
  • BuildAndLaunch.md and Docs/Scenes.md - companion references for general launch selection and scene-debug behavior

Validation and Tests

Current checks worth running when Android build, packaging, or launch docs change:

  • ../../.vscode/tasks.json confirms the current task names, package paths, APK paths, and remote-scene sequence.
  • README.md remains the front-door summary for the concise Android command flow; this guide owns detailed Wi-Fi ADB, Gradle project, package-output, and remote-scene troubleshooting behavior.
  • the embedding project's CMakeLists.txt confirms which package targets emit Android APKs (those with BINARY Client Android arm64 Apk).
  • the embedding project's CI config confirms it prepares the Android workspace only for APK-producing package types.
  • ../BuildTools/android_device.py confirms the current helper commands and failure messages around Wi-Fi ADB discovery/installation.
  • ../BuildTools/package.py confirms Android config keys, icon requirements, signing behavior, and resource movement into APK assets.
  • FOnlineActivity.java confirms APK asset-path forwarding, the private writable root and ClientNetwork.ServerHost override handling.

See Also

  • BuildAndLaunch.md — general build and launch entry points
  • Scenes.md — SceneLaunch vs RemoteSceneLaunch behavior
  • WebDebugging.md — sibling remote-scene workflow for browser clients
  • GuiSystem.md and Localization.md — generated-screen and text-presentation references when APK/device startup is healthy but UI output is wrong