This directory contains the first editable skeleton for the extracted Android compatibility MOD / Harmony patcher. It is intentionally not a copy of the old full game source.
Current implementation (STS2AndroidPortCompat):
ModEntryexposes the same unmanaged entrypoints used by the reference launcher (InitializeGodotSharp,Apply).PlatformPatchesdisables desktop Steam/Sentry/platform paths.RunHistoryPatcheskeeps persistedPlatformType.Steamrun histories readable without loading desktopsteam_api64: only history-screen identity lookups use the Android/null-platform ID and cached-name fallback, while Steam transport, lobby, and invite behavior remains untouched.ReleaseInfoPatchesreadsrelease_info.jsonfrom the imported private payload atOS.GetDataDir()/game/release_info.json.AndroidSettingsBridgereads extra-settings JSON fromOS.GetDataDir()/default/1/settings.savewithout requiring PCSettingsSaveto contain Android-only fields.AndroidSettingsPatchesmaps companion JSON fields that also exist in the PCSettingsSave(aspect_ratio,vsync,msaa,fps_limit,fullscreen), maps companionmod_settings.mods_enabled/mod_list/ legacydisabled_modsinto the runtimeModSettings, and merges Android-only JSON keys back after PCSettingsSaveserialization would drop them.DisplaySettingsPatchesapplies Android-only companion fields for FPS, global content scale, UI font scale, and landscape orientation. It is the sole coordinator for root-windowContentScaleMode,ContentScaleAspect, andContentScaleSize; logical layout always usesCanvasItems, with the ownership orderFixedAspect > UiScaleAuto. Auto uses the UI-scale target and fixed aspect uses its corresponding fixed target.fullscreen_render_sizenever owns or replaces that logical target and Java no longer forwards it as Godot--resolution; changing it in the in-game settings immediately resizes only the root renderer render target. After all high-levelContentScale*setters finish, the coordinator appliesRenderingServer.ViewportSetRenderDirectToScreen(false),ViewportSetSize(), andViewportSetGlobalCanvasTransform(). The sceneWindow, its input transform, and the AndroidSurfacestay unchanged. Do not useSurfaceHolder.setFixedSize()orViewportAttachToScreen()for this path.0x0restores both the native attachment-sized render target and the base canvas transform. A non-zero preset is a minimum reference rectangle: the effective target keeps the current native attachment aspect and uses Expand-style coverage (for example, native2400x1080plus1280x720becomes1600x720). The custom longest-dimension cap ismax(4096, native longest dimension). Root-windowSizeChanged, application resume, and consistency repair reapply the renderer state after logical setters. Ownership is published before any compare-before-set Window mutation, reentrant requests are coalesced, and application resume schedules one deferred runtime apply instead of rebuilding the viewport from focus notifications.UiScalePatchesonly supplies the Auto target and requests a single-flight recalculation; it never writesContentScale*directly.global_scaleremains an independentContentScaleFactorunder every owner, and UI font scale remains independent. Each resume generation performs one deferred consistency check and at most one compare-before-set repair; stale targets are rejected by revision and a failed final check only logs a warning instead of entering a viewport rebuild loop.MobileHandLayoutPatchesapplies the companionshow_more_hand_card_text/show_more_hand_card_text_lift_height_percenthand lift as a Harmony post-layout offset without rebuilding the game body.DevTools/hosts the file-based Java bridge for the in-game overlay. It is started independently from optional version-specific feature patches, writes alauncher/devtools/host.jsonready marker, and answers protocol-2 requests in their own atomicresponse-<uuid>.jsonfiles (while retaining legacyresponse.jsoncompatibility): reflection inspector, collapsible Godot scene tree, nested Godot object / node property inspection, and temporary GDScript execution with non-Nil result capture, companion settings runtime apply, and overlay quick-restart.QuickRestartPatchesadds the built-in Android retry button on the pause menu whenquick_sl_enabledis true and no external Quick Restart UI mod is loaded; it waits for pending run-save work, awaits saved-run setup before loading the new run, and fades back in on failure so async restart errors do not leave a permanent black transition screen.ExternalSettingsPatchesadds a fallback in-game settings row that opens the Java companion settings shell, redirects game Quit back to the settings shell, and applies the companionpending_unlock_all.flagcommand.ModLoaderPatchesredirects local mods toOS.GetDataDir()/modsand skips Steam mod enumeration.DeferredModPatchQueueprotects Android/Mono from user-MOD patches that eagerly initialize STS2 UI/Godot types before essential startup. It covers both directPatchProcessor.Patch()calls and the per-target privatePatchClassProcessor.ProcessPatchJob()path used byHarmony.PatchAll(). Safe/model targets remain immediate; unsafe jobs retain their original Harmony owner, patch lists, ordering, and per-target prepare/cleanup flow and replay once after model/network type initialization. A syntheticsts2fixture regression is available throughtools/test-deferred-mod-patch-queue.sh.ShaderCompatibilityPatchesloadsport_compat.pckand applies the mobile shader replacements copied from the old port whenshader_compatibility_modeis enabled; it intentionally keeps the originalcanvas_group_mask_blur.gdshadercard/Ancient-card face shader and does not ship the old mobile substitute because it can render Ancient card faces solid white.TouchInputPatchesadds the first touch-friendly card-play cancellation path for releases outside the play zone / untargeted releases.MobileTapPreviewPatchesadds a first-pass tap-to-lift card preview flow using companiontouch_lift_preview/touch_lift_retap_actionsettings.AndroidInputCompatPatchesbridges Android back-button, two-finger inspect right-click, and trigger-axis controller compatibility into original input.LanMultiplayerPatchesbridges companion LAN settings while leaving the originalMessageTypesID assignment andNetMessageBusserialization/deserialization untouched. It adds configured compatibility mod names to multiplayer checks, honors persistent/custom LAN player IDs, replaces the no-Steam join screen with host/port input, and hosts ENet games with the configured player capacity.
Build locally from the parent repository after configuring .env:
../tools/android/build-port-mod.shOr build the schema-2 family compatibility pack from this submodule with local environment variables:
export DOTNET_BIN=/path/to/dotnet
export STS2_ORIGINAL_V1080_REFERENCE_DIR=/path/to/original-v0.108.0/bin/Debug
./tools/build-compat-matrix.sh --target v0.108.0
# Historical V1090 names identify the shared v0.109.x target; use the latest v0.109.1 gate.
export STS2_ORIGINAL_V1090_REFERENCE_DIR=/path/to/original-v0.109.1/bin/Debug
./tools/build-compat-matrix.sh --target v0.109.0
# Shared current v0.110.x public-beta API/protocol target.
# Keep the historical V1100 variable/flavor name; point it at the latest v0.110.1 gate.
export STS2_ORIGINAL_V1100_REFERENCE_DIR=/path/to/original-v0.110.1/bin/Debug
./tools/build-compat-matrix.sh --target v0.110.0tools/build-compat-pack.sh is the legacy schema-1 path; use it only when also providing a matching COMPAT_MANIFEST.
The patched Godot runtime expects STS2Mobile.dll / STS2Mobile.ModEntry; the parent build script builds this skeleton under that assembly name and copies it into android/assets/dotnet_bcl/.