This repository is a full copy of Ekyso/StS2-Launcher, created to continue and broaden development as StS2 Mobile: a focused Android rewrite project. The goal is a drastic architecture and reliability overhaul that is harder to do in the upstream repo while maintaining compatibility and delivering incremental improvements back when possible.
- Migration checklist: MIGRATION_CHECKLIST.md
- Overhaul plan: OVERHAUL_ROADMAP.md
- Contribution process: CONTRIBUTING.md
- Overhaul status: OVERHAUL_STATUS.md
- Changelog: CHANGELOG.md
- Device log checklist: docs/device-log-checklist.md
- Android runtime findings: docs/android-runtime-findings.md
- Current Android status: docs/current-android-status.md
- Testing needed: docs/testing-needed.md
git remote rename origin upstream
git remote add origin https://github.com/SocialHummingbird/StS2-Launcher-Overhaul.git
git remote set-url origin https://github.com/SocialHummingbird/StS2-Launcher-Overhaul.git
git fetch origin
git fetch upstreamAn Android launcher for Slay the Spire 2, built on a custom Godot 4.5.1 engine with .NET/Mono and Harmony runtime patching.
Disclaimer: This is an unofficial community project. Slay the Spire 2 is developed and published by Mega Crit Games. A valid Steam account that owns Slay the Spire 2 is required. Game files are downloaded directly from Steam after authentication. No game assets are included in this repository.
Current state: StS2 Mobile is playable on tested ARM64 Android hardware, but it is still a prerelease community launcher. The focus is now stability, loading speed, cleaner onboarding, branch switching, Steam Cloud safety, and experimental Workshop/mod support.
Latest published APK prerelease: v0.2.335-mod-selector-deps-cloud-marker-debug
- APK asset:
StS2Launcher-v0.2.335-mod-selector-deps-cloud-marker-debug-arm64-v8a.apk - Package name:
com.sts2launcher.overhaul.fork.local - Version code:
335000 - SHA-256:
46799153565feb414a702d590e15cc29bea2cfb5437eb4a11fa5606587db2ac1 - Signing channel: local debug/test channel
What currently works on tested ARM64 hardware:
- Steam login reaches Steam Guard/authentication flow.
- Game files can be downloaded from Steam for owned accounts.
- Public/default game launch has ARM64 evidence.
- Public-beta branch launch has ARM64 evidence with matched beta PCK and matched beta runtime pack.
- Steam Cloud Pull into Android local app storage has been validated.
- Steam Cloud Push is intentionally guarded and is not automatic.
- The launcher has a first-class Mods section on the main play screen.
- Workshop/staged mods can be selected, disabled, and launched through the Android runtime mod-loader path.
BaseLib,Quick Restart, and manually importedVanilla and Modded Saves Mergerhave reached the game main menu in ARM64 validation.
Experimental or still hardening:
- Workshop/mod support is functional but not finished. Steam discovery can stage some subscribed mods, but items exposed only as legacy UGC handles may still need manual import.
Vanilla and Modded Saves Mergeris the most important current mod validation target. It has manual-import launch evidence, but broader save-merge compatibility still needs tester reports.- Branch switching is implemented, but beta/password/private branch behavior and save compatibility across branches still need more device evidence.
- Samsung/One UI layouts and login behavior need more current-version reports from affected users.
- Startup/loading speed and diagnostics are active refactor targets.
Steam Cloud safety:
- Pull from Steam Cloud is the preferred first action.
- Push to Steam Cloud is never run automatically.
- Manual Push is gated because it can overwrite Steam Cloud state.
- Push remains locked in active modded-save risk states until the launcher has enough evidence that the local save state is safe.
How to help:
-
Download the latest APK from Releases.
-
Read Testing needed before filing results.
-
Use the focused GitHub issue templates for crashes, Steam Cloud, branch/download issues, mods/save-merger testing, or device compatibility reports.
-
Emulator limitation: Android
x86_64is fallback/diagnostic coverage only. ARM64 hardware remains the proof target.
See docs/current-android-status.md for the current evidence and remaining blockers.
- Steam authentication
Login via SteamKit2 with Steam Guard 2FA support. Android now uses an integrated in-app native Steam credential panel with real username/password fields, Android credential-provider hints, accessible field labels, inline status/error guidance, keyboard-safe scrollable layout, and stacked full-width touch controls where supported. The launcher does not store or inject Steam passwords, and native username/password fields are cleared after submit/cancel/expiry. ARM64 device validation has progressed through authenticated download, cloud pull, local hardening Push, and game launch; password-manager suggestion behavior in the native panel still needs broader device reports. - Game file download
Depot download directly from Steam, with update checking, an ARM64-validated responsive progress screen, Steam branch/version dropdown selection, a non-mutatingRefresh Game Versionsaction that reads account-visible Steam app-info branch metadata, and side-by-side cached installs for non-public branches. The portal explicitly separates local version download/update actions from Steam Cloud save actions and collapses verbose version details on compact screens. Beta/version support is currently a hardening feature: dropdown labels stay concise but can show ready/build/password/unavailable badges, selected-version helper text surfaces availability/password/build metadata where Steam exposes it, known unavailable branches are blocked before game-version download/update attempts,public-betahas local ARM64 launch proof from its side-by-side cache, and Steam beta password entry is not implemented. - Cloud saves
Steam cloud sync via SteamKit2's CCloud API, with timestamp-aware conflict resolution and non-blocking background uploads. Pull from Cloud, Push to Cloud, and Pull-after-Push round trip are validated on ARM64 local hardening builds. The portal labels Pull as Steam Cloud to Android and Push as Android saves to Steam Cloud, places Pull before Push so the safer baseline action is visually first, keeps those primary cloud actions above lower-frequency cloud options, and collapses cloud-safety guidance/options on compact screens to reduce clutter. Push remains an explicit overwrite-risk action because it can replace Steam Cloud state, requires an overwrite confirmation arming tap before the final confirmation, shows an armed overwrite warning before the final confirmation, and now gates manual Push on current-version Pull evidence plus Android local save evidence before upload. Branch-switch Push adds stricter selected-version Pull/local-save/backup evidence gates. - Steam Workshop mods
Subscribed Workshop mods can be synced into app-private Android storage and loaded by the runtime mod-loader patch. Current ARM64 evidence covers public-beta, core-release, and public-after-beta branch switching with matched selected PCK/runtime evidence;BaseLibandQuick Restartare staged and scanned from Workshop storage. The previous BaseLib initializer hard failure is handled by an Android compatibility filter that skips known incompatible BaseLib patch classes, so those skipped BaseLib features still need follow-up. Workshop sync and clear do not run Steam Cloud Push, and manual Push is locked while active staged Workshop PCK mods are present. The launcher now has a first-class Mods section on the main play screen with active-mod status, unsupported-item attention text, manual import guidance, and Workshop sync/clear actions. - Mobile adaptation
Touch input, short-edge-aware launcher scaling, responsive ready/download/login layouts, larger touch-first action targets, task-led primary action wording, consistentStart Gameprimary CTA, high-contrast rounded actions, a branded atmospheric backdrop, mobile-first compact panel sizing, dynamic compact content width, reduced compact header chrome, compact section headers, compact button labels, a phase-labeled status-led launcher portal with a structured phase chip, error-first guided next-action label, and compact vertical next-step hero, collapsible safe first-run guidance on compact screens, titled Steam sign-in/install/play-sync sections, hidden diagnostics by default, and app lifecycle handling via Harmony runtime patches. - LAN multiplayer
UDP broadcast discovery and manual IP join. - Shader warmup
Vulkan pipeline cache persistence and canvas ubershader support to eliminate first-encounter stutters. - Credential security
Steam refresh tokens encrypted at rest via Android Keystore (AES-256-GCM, hardware-backed TEE). Steam passwords are not stored or injected by the launcher, and SteamKit debug logs are disabled by default; when explicitly enabled for diagnostics, they are sanitized before entering launcher diagnostics.
At startup, STS2Mobile.dll is loaded via coreclr_create_delegate and applies Harmony patches to adapt the desktop game for mobile. The launcher intercepts GameStartupWrapper() to present a Steam login screen before the game starts.
- Launcher-only mode
If no game files are present, the app loads a minimalbootstrap.pckand shows the launcher UI for Steam login and game download. - Normal mode
With game files downloaded, all patches apply againststs2.dlland the game runs natively after authentication.
Custom patches to the Godot 4.5.1 engine source for Android-specific issues:
- Vulkan pipeline cache persistence
Saves compiled pipelines when the app loses focus, preventing recompilation after Android kills the process. - Canvas ubershaders
Enable ubershader fallback for 2D rendering, eliminating first-encounter VFX stutters from blocking pipeline compilation.
src/STS2Mobile/
ModEntry.cs # Entry point ([UnmanagedCallersOnly] Apply())
PatchHelper.cs # Shared patch utility + logging
Patches/ # Harmony patches (one file per concern)
Launcher/ # Programmatic Godot UI (MVC)
Steam/ # SteamKit2 login, depot download, cloud saves
android/ # Godot Android gradle project
src/.../GodotApp.java # Activity, assembly setup, Keystore encryption
assets/bootstrap.pck # Minimal PCK for launcher-only mode
src/stubs/ # Native library stubs (Steam API, Sentry)
scripts/ # Build and tooling scripts
- .NET 9 SDK
- Android SDK + NDK (see
android/config.gradlefor versions) - Python 3 (for
make-bootstrap-pck.pyand SCons) - Original game files in
upstream/godot-export/ - Custom Godot engine build (see
scripts/build-godot.sh) - FMOD SDK in
vendor/fmod-sdk/
Note: This is a WIP. There are other binaries that are required and will fail if you just run the ./build.sh script. Godot Engine can be found on their repo https://github.com/godotengine/godot. Harmony can be found here https://github.com/Ekyso/Harmony but the version used in StS2 Launcher is compiled using dotnet 9.0. FMOD can be found here https://www.fmod.com/. Spine can be found here https://esotericsoftware.com/. I plan to upload the custom fork of Godot Engine used and the dotnet 9.0 Harmony soon. However, Spine and FMOD will not be uploaded due to licensing restrictions. Information on licensing can be found in the THIRD-PARTY-NOTICES.txt of the root folder.
bash scripts/build.shThis runs the full pipeline:
dotnet publishthe patcher (outputsSTS2Mobile.dll+ SteamKit2 dependencies)- Copies published DLLs to
android/assets/dotnet_bcl/ - Copies
libSystem.Security.Cryptography.Native.Android.soto JNI libs (for TLS) - Bumps the version in
gradle.properties - Builds the APK via
./gradlew assembleMonoRelease
Output: android/build/outputs/apk/mono/release/StS2Launcher-v<version>.apk
For local Android testing, use the PowerShell build wrapper so the managed assemblies, patched SteamKit dependency, Mono Android runtime libraries, and native ABI selection stay in sync:
.\scripts\build-android-local.ps1 -VersionName "0.2.0-local-x86" -VersionCode 200 -Abi x86_64
.\scripts\build-android-local.ps1 -VersionName "0.2.0-local-arm64" -VersionCode 201 -Abi arm64-v8a
.\scripts\build-android-local.ps1 -VersionName "0.2.0-local-universal" -VersionCode 202 -Abi universalThe Gradle output directory only keeps the most recent mono release APK. The wrapper also archives every local build to artifacts/android/ with the ABI in the filename, for example:
artifacts/android/StS2Launcher-v0.2.0-local-x86-x86_64.apk
artifacts/android/StS2Launcher-v0.2.0-local-x86-x86_64.apk.sha256
artifacts/android/StS2Launcher-v0.2.0-local-arm64-arm64-v8a.apk
artifacts/android/StS2Launcher-v0.2.0-local-arm64-arm64-v8a.apk.sha256
artifacts/android/StS2Launcher-v0.2.0-local-universal-universal.apk
artifacts/android/StS2Launcher-v0.2.0-local-universal-universal.apk.sha256
Verify a local archived APK from artifacts/android/:
sha256sum -c StS2Launcher-v0.2.0-local-arm64-arm64-v8a.apk.sha256The Android x86_64 emulator is useful for install, routing, release packaging, and native diagnostic-screen testing, but it is not a valid proof target for the Godot/.NET launcher or downloaded game. x86_64 routes to a native fallback screen instead of starting Godot, because the emulator path crashes inside the Mono/GodotSharp native runtime. A forced-Godot emulator run can still crash with native runtime failures such as destroyed mutex access. Use an arm64-v8a Android device/build to test Steam login, download, and actual game launch.
Once adb devices shows exactly one attached device or emulator, run:
.\scripts\test-android-local.ps1The smoke-test script selects the newest archived APK matching the attached device ABI, installs it, launches LauncherActivity, captures logcat to artifacts/android/logcat-smoke-*-full.txt, writes a focused subset to artifacts/android/logcat-smoke-*-filtered.txt, writes a handoff summary to artifacts/android/logcat-smoke-*-summary.txt, and reports whether it saw the native x86 fallback route or crash markers.
If the selected APK has a .sha256 sidecar, the script verifies it before install and stops on mismatch.
By default, local builds and the smoke-test script use package com.sts2launcher.overhaul.fork.dev.
For a clean app-data run:
.\scripts\test-android-local.ps1 -ClearAppDataIf the emulator is still booting or ADB is slow to attach, wait before failing:
.\scripts\test-android-local.ps1 -WaitForDeviceSeconds 60If more than one device/emulator is attached, pass the target serial:
.\scripts\test-android-local.ps1 -DeviceSerial emulator-5554adb install -r android/build/outputs/apk/mono/release/StS2Launcher-v*.apk
# Fresh install for local build wrapper default package
adb shell pm clear com.sts2launcher.overhaul.fork.dev
# Fresh install for future production package, if used
adb shell pm clear com.sts2launcher.overhaul.forkGitHub Actions now builds Android APKs and publishes them to Releases.
- Open the repository Releases page: https://github.com/SocialHummingbird/StS2-Launcher-Overhaul/releases
- Download the APK for the latest release.
- Direct latest URL: https://github.com/SocialHummingbird/StS2-Launcher-Overhaul/releases/latest
- Current release assets are ARM64-only test packages, named like:
StS2Launcher-v<version>-arm64-v8a.apk
- Older releases may include universal or x86_64 assets. Prefer ARM64 for phones.
Current published APK release:
.\scripts\verify-android-release-apk.ps1 `
-ReleaseTag "v0.2.335-mod-selector-deps-cloud-marker-debug" `
-AssetName "StS2Launcher-v0.2.335-mod-selector-deps-cloud-marker-debug-arm64-v8a.apk" `
-Abi arm64-v8a
.\scripts\install-android-release.ps1 `
-ReleaseTag "v0.2.335-mod-selector-deps-cloud-marker-debug" `
-AssetName "StS2Launcher-v0.2.335-mod-selector-deps-cloud-marker-debug-arm64-v8a.apk" `
-ClearAppData `
-Launch `
-CaptureDiagnosticsRelease details:
Release: v0.2.335-mod-selector-deps-cloud-marker-debug
Asset: StS2Launcher-v0.2.335-mod-selector-deps-cloud-marker-debug-arm64-v8a.apk
Package: com.sts2launcher.overhaul.fork.local
VersionName: 0.2.335-mod-selector-deps-cloud-marker-debug
VersionCode: 335000
SHA-256: 46799153565feb414a702d590e15cc29bea2cfb5437eb4a11fa5606587db2ac1
The verifier downloads the GitHub release asset, checks its release SHA-256 digest, confirms the expected native libraries are present, and checks that libgodot_android.so contains the Android app-data .NET assembly lookup marker rather than the stale PCK lookup marker.
Safe public trial checklist:
- Use an ARM64 Android phone. Current public APKs are not x86_64 emulator proof.
- Install the latest GitHub release APK from the Releases page.
- Log in only with a Steam account that owns Slay the Spire 2.
- Download the game through the launcher.
- Use Pull from Cloud before Push to Cloud.
- Confirm Android local saves/profiles exist before using Push to Cloud.
- Treat Push to Cloud as destructive: it makes Steam Cloud reflect Android local saves, can overwrite remote save state, now requires an
ARE YOU SURE?arming tap, and still requires the final confirmation dialog.
Support boundaries for public testers:
- This is an unofficial community launcher and does not include game assets.
- Do not post Steam credentials, guard codes, refresh tokens, private save data, or full unsanitized logs in public issues or Reddit threads.
- Current support target is ARM64 Android hardware. x86_64 emulator behavior is diagnostic-only.
- If reporting a cloud-save issue, say whether you used Pull or Push, but scrub usernames, account IDs, and save contents first.
- Optional manual checksum verification:
sha256sum -c StS2Launcher-vX.Y.Z-arm64-v8a.apk.sha256- Optional manual install:
adb install -r StS2Launcher-vX.Y.Z-arm64-v8a.apkSigning behavior:
- Published APK releases require repository signing secrets and a pinned release signer fingerprint.
- The release workflow refuses to publish a temporary-key APK because it would not update existing installs safely.
Known current runtime limitations:
- The app now has a validated working ARM64 path through download, cloud pull, cloud push hardening, and game launch, but this is not yet a finished release-candidate pass.
- Push to Cloud is locally validated after the managed SHA-1 hardening fix, and that fix is included in the verified public APK line. Repeat Push confirmation/cancel smoke on the newest public APK is still required before release-candidate signoff.
- The public release package has passed update-compatible release builds through
v0.2.187-beta-art-fallback; the newest APK also has ARM64 visual validation for the responsive launcher download-progress and ready states plus Push confirmation/cancel path. Repeated local.localin-place upgrade coverage remains secondary because local builds use a separate package identity. - Stale assembly cache behavior still needs repeated local upgrade coverage after signing continuity is fixed.
x86_64emulator validation is fallback/diagnostic coverage only unless explicitly forcing Godot for crash investigation.
If installation fails:
-
INSTALL_PARSE_FAILED_NO_CERTIFICATESor signature errors:- likely a partially downloaded APK or signing mismatch.
- re-download and re-run
sha256sum -c.
-
INSTALL_FAILED_UPDATE_INCOMPATIBLE:- remove the previous install first, then reinstall. Current test releases use package
com.sts2launcher.overhaul.fork.dev:
adb uninstall com.sts2launcher.overhaul.fork.dev adb install -r StS2Launcher-vX.Y.Z-arm64-v8a.apk
- remove the previous install first, then reinstall. Current test releases use package
-
INSTALL_FAILED_OLDER_SDK:- your device is running an unsupported Android API level.
-
App isn't compatible with your phone:- make sure you downloaded an APK matching your device ABI. Current public test releases are ARM64-only.
-
INSTALL_FAILED_DEXOPTor immediate crash:- capture logs with
adb logcatand open a release issue with stack trace.
- capture logs with
Maintainers can trigger the release workflow manually from the Actions tab or let it run automatically when pushing tags like v1.2.3.
- Tag-based publish:
- Push
vX.Y.Ztomain.
- Push
- Manual publish:
workflow_dispatchinput fields support overridingrelease_tag,package_name, version name/code, and whether to create the GitHub release.
- Required release signing:
- Configure repository secrets:
ANDROID_RELEASE_KEYSTORE_BASE64ANDROID_RELEASE_KEYSTORE_PASSWORDANDROID_RELEASE_KEY_ALIAS
- Configure repository variable:
ANDROID_RELEASE_SIGNER_SHA256
- Configure repository secrets:
If signing secrets or ANDROID_RELEASE_SIGNER_SHA256 are missing, the workflow refuses to publish. This prevents GitHub from creating APKs that cannot update the installed app.
Use the helper script to configure GitHub from a stable release keystore:
.\scripts\configure-android-release-signing.ps1 `
-KeystorePath C:\path\to\release.keystore `
-KeystorePassword "<password>" `
-KeyAlias "<alias>"Check whether GitHub is ready to publish update-compatible APKs:
.\scripts\check-android-release-readiness.ps1The release workflow also verifies the built APK against a previous GitHub release APK before upload. It fails if the package name changes, the signing certificate changes, or versionCode does not increase. If the current public APK was signed with a temporary key, create one explicit stable-signing baseline release with allow_update_baseline_reset=true; direct update from the temporary-key APK is impossible, but later GitHub releases will be pinned to the stable signer.
Release validation checklist for every release is tracked in docs/android-release-validation.md.
# Regenerate bootstrap PCK (only if project.godot changes)
python3 scripts/make-bootstrap-pck.py
# Rebuild Godot engine (only if engine source changes)
bash scripts/setup-godot-source.sh
bash scripts/build-godot.sh
# Windows PowerShell equivalent
.\scripts\setup-godot-source.ps1
.\scripts\build-godot.ps1
# Rebuild native stubs (requires Android NDK)
bash src/stubs/build_stubs.shscripts/setup-godot-source.sh or scripts/setup-godot-source.ps1 restores vendor/godot and the Python/SCons virtualenv needed by the matching build script. By default it checks out upstream Godot 4.5.1-stable; set GODOT_REPO and GODOT_REF if you have the original patched engine fork. Emulator x86_64 testing requires arm64-v8a and x86_64 libgodot_android.so to be built from the same engine checkout.
Both devices must be on the same local network. The mobile app discovers nearby games via UDP broadcast, or you can enter the PC's IP address manually.
On the PC, add --fastmp to the Steam launch options:
Steam > Slay the Spire 2 > Properties > Launch Options and enter --fastmp
This enables the fast multiplayer mode that the mobile client expects.
- Native library stubs (
src/stubs/) provide no-op.sofiles for desktop-only libraries (Steamworks SDK, Sentry) so the linker is satisfied at runtime. - The bootstrap PCK is a minimal
project.godotwrapper that enables .NET module initialization without game files. - The game's Sentry plugin has no
android.arm64build, so it's disabled via PCK patching and Harmony patches. - GodotSharp interop is manually bootstrapped in
ModEntry.cssince the Godot SDK source generators aren't available.
This project is licensed under the MIT License. See THIRD_PARTY_LICENSES.md for third-party dependency licenses.
FMOD requires a commercial license if your project generates revenue. Spine Runtimes require a valid Spine Editor license. See the third-party licenses file for details.