16 KiB

name, description
name description
th1-ios-migration TH1 project-specific iOS migration guide for same-mainline dual-platform support: HybridCLR hot update foundation, iOS/IL2CPP compile isolation, Steamworks/Steam SDK platform abstraction without Steam regressions, touch input adaptation, YooAsset AssetBundle/resource migration, BundleResources DataAssets -> Export packaging sync, iOS build settings, unified PC/iOS build window flow, and verification that existing online Steam builds and gameplay behavior remain unaffected. Use whenever Codex works on TH1 iOS packaging, mobile porting, HybridCLR, YooAsset AB/resource loading, removing direct Steamworks references for iOS, platform services, touch controls, or build pipeline changes that must preserve the current Steam version.

TH1 iOS Migration

Core Rule

TH1 iOS migration is same-mainline dual-platform work. Do not create a long-lived Steam code line and a separate mobile code line.

Every change must preserve the existing Steam build and gameplay behavior while adding iOS support through platform-specific implementations, build defines, asmdef/platform settings, and no-op/mobile services.

Default target shape:

  • Shared gameplay, action logic, AI, MapData, config, save data, and localization stay common.
  • Steam SDK, Steam lobby/P2P, Workshop, Steam auth, and Steam-specific UI live behind Steam-only platform boundaries.
  • iOS gets IL2CPP/AOT-compatible code, mobile/no-op platform services, touch input, and platform-specific resources.
  • Steam and iOS may have separate build profiles, resource bundles, and release branches, but not divergent business logic.

First Reads

Read the relevant files before editing:

  • MD/GameMDFramework/00-主文档-游戏架构总览.md
  • MD/GameMDFramework/11-网络与Steam.md
  • MD/GameMDFramework/13-配置与数据资产.md
  • Unity/Packages/manifest.json
  • Unity/ProjectSettings/ProjectSettings.asset
  • Unity/Assets/Scripts/TH1_Logic/Core/Main.cs
  • Unity/Assets/Scripts/TH1_Logic/Net/ILobby.cs
  • Unity/Assets/Scripts/TH1_Logic/Net/LobbyManager.cs
  • Unity/Assets/Scripts/TH1_Logic/Steam/SteamLobbyManager.cs
  • Unity/Assets/com.rlabrecque.steamworks.net/Runtime/com.rlabrecque.steamworks.net.asmdef
  • Unity/Assets/Scripts/TH1_Logic/Input/InputLogic.cs
  • Unity/Assets/Scripts/TH1_UI/CameraController.cs
  • Unity/Assets/Scripts/TH1_UI/View/Base/View.cs
  • Unity/Assets/Scripts/TH1_Logic/Rersource/ResourceManager.cs
  • Unity/Assets/AssetBundleCollectorSetting.asset
  • Unity/Assets/Scripts/TH1_Resource/ResourceCache.cs
  • Unity/Assets/Scripts/TH1_Logic/Config/ConfigManager.cs

Also use:

  • th1-network-sync when touching lobby, P2P, GameNetSender, GameNetReceiver, NetData, action sync, reconnect, or multiplayer start/resume.
  • th1-action-logic when changing authoritative gameplay, action execution, AI action flow, replay, or deterministic behavior.
  • th1-multilingual when changing Workshop/local mod language loading or localization resources.
  • th1-server-backend when replacing Steam auth, STS, OSS upload, bug report upload, or backend identity.

Migration Order

Use this order unless the user explicitly changes it:

  1. Add HybridCLR foundation.
  2. Clean iOS compile isolation and Steam platform boundaries.
  3. Add touch/mobile input adaptation.
  4. Migrate resources to YooAsset AssetBundle flow.
  5. Add automated Steam/iOS build and hot-update packaging verification.

Do not use AB/YooAsset migration as a way to hide platform compile errors. iOS compile isolation must become clean before large resource replacement.

Phase 0: Baseline And Steam Parity Contract

Before editing:

  • Run git status --short and preserve unrelated user changes.
  • Search direct platform dependencies with rg -n "using Steamworks|SteamUser|SteamFriends|SteamUtils|SteamUGC|SteamAPI|SteamManager|TH1_Logic\\.Steam|UNITY_IOS|STEAM_CHANNEL|Resources\\.Load|Input\\.Get|mousePosition|safeArea" Unity/Assets/Scripts.
  • Identify whether the change is platform boundary, hotfix assembly layout, touch input, resource loading, backend auth, or build settings.
  • Write down which Steam behavior must remain identical: lobby, P2P, Workshop, achievements, bug report upload, OSS collect upload, config/save, UI visibility, and multiplayer action sync.

Steam parity rules:

  • Do not delete Steamworks.NET or Steam-specific scripts as part of iOS migration.
  • Do not remove STEAM_CHANNEL behavior for Standalone.
  • Do not change multiplayer state machines unless the task is explicitly network work and th1-network-sync is active.
  • Do not move authoritative action logic into hotfix assemblies unless the user explicitly accepts the extra deterministic and replay risk.
  • Do not modify obfuscation, MemoryPack compatibility, or generated config outputs without repeated explicit confirmation from the user.

Phase 1: HybridCLR Foundation

Goal: create a hot-update pipeline without changing Steam gameplay.

Preferred first scope:

  • Add HybridCLR package/config/build steps.
  • Run the HybridCLR Installer before GenerateAll on any fresh machine, package re-resolution, or HybridCLR package update. In TH1 use Tools/TH1/iOS Migration/HybridCLR/1. Run HybridCLR Installer, then click Install.
  • Create hotfix assembly boundaries such as TH1.Hotfix only after deciding what belongs there.
  • Keep authoritative turn/action/AI/network contracts in AOT main assemblies at first.
  • Put low-risk UI, presentation helpers, non-networked bug fixes, or feature flags into hotfix first.
  • Generate and commit required AOT metadata/link preservation files only through the documented HybridCLR workflow.
  • Ensure the Steam build can still run without downloading any hotfix package when using local/editor mode.

iOS constraints:

  • iOS is IL2CPP/AOT. Assume reflection, generic sharing, MemoryPack formatters, and dynamically referenced types can be stripped unless preserved.
  • Treat code hot update on iOS as sensitive. Do not present it as a way to bypass App Store review. Prefer resources/configs for content updates and use code hotfix cautiously.
  • Keep a rollback path: base app version, hotfix manifest version, minimum compatible app version, and ability to ignore bad hotfix packages.

Checks:

  • Steam Standalone compile still succeeds.
  • iOS IL2CPP compile reaches the next real blocker.
  • Hotfix assembly does not reference Steamworks or editor-only assemblies.
  • link.xml/preserve additions are minimal and justified.
  • HybridCLRData/AssembliesPostIl2CppStrip/<BuildTarget> exists after Generate All, and required AOT metadata has been copied to Assets/StreamingAssets/HybridCLR/AOTAssemblies.
  • If HybridCLR/Generate/All failed because HybridCLR was not installed, do not continue into AB packaging. Fix the installer/generate step first.

Phase 2: iOS Compile Isolation And Steam Boundary

Goal: iOS compiles without Steamworks types while Steam keeps full behavior.

Preferred pattern:

  • Move platform-neutral DTOs out of TH1_Logic.Steam into TH1_Logic.Net or a platform-neutral namespace before sharing them through ILobby.
  • Make ILobby and shared UI/data contracts free of Steamworks types.
  • Keep SteamLobbyManager, SimpleP2P, GameNetSender, GameNetReceiver, Workshop browser/uploader, and Steam auth under Steam-only compile/platform boundaries.
  • Add mobile/no-op implementations for services iOS cannot use yet.
  • Hide or disable unsupported iOS UI entries instead of leaving clickable failing paths.

Use platform services for direct SDK access:

  • IPlatformUser: user id, display name, login state.
  • IPlatformLobby/existing ILobby: lobby/multiplayer operations.
  • IPlatformAchievement: local/Steam/Game Center achievements.
  • IPlatformWorkshop or local mod service: Workshop and local mod separation.
  • IPlatformAuth: Steam ticket, Game Center/apple/backend/anonymous auth.
  • IPlatformBackendUpload: OSS/bug report/collect upload routing.

Compile guard guidance:

  • Prefer asmdef include platforms and service implementations over scattered #if.
  • Use #if STEAM_CHANNEL || UNITY_STANDALONE only around Steam-specific code, not around shared gameplay.
  • Do not make UNITY_EDITOR automatically mean Steam behavior if that blocks iOS simulation or mobile service testing in editor.
  • Keep editor-only tooling under editor asmdefs or #if UNITY_EDITOR.

Checks:

  • rg -n "using Steamworks|SteamUser|SteamFriends|SteamUtils|SteamUGC|SteamAPI|SteamManager" Unity/Assets/Scripts -g '!Steamworks.NET/**' should show only Steam-only files or files guarded out of iOS.
  • LobbyManager selects Steam for Steam builds and no-op/mobile for iOS.
  • iOS build does not compile against the Steamworks.NET asmdef.
  • Steam multiplayer, Workshop, and Steam upload paths are unchanged or covered by explicit parity tests.

Phase 3: Touch And Mobile Input

Goal: single-player iOS is playable without keyboard/mouse.

Touch work is a UX layer, not a gameplay mutation layer:

  • Do not bypass action construction or CheckCan/CompleteExecute.
  • Convert tap/drag/pinch/cancel/confirm into the same input/action intentions used by existing UI and InputLogic.
  • Keep keyboard/mouse shortcuts working on Steam.
  • Add touch-specific affordances when keyboard shortcuts previously exposed required commands.

Minimum iOS control coverage:

  • Tap grid/unit/city selection.
  • Tap action buttons and confirm/cancel flows.
  • Drag camera.
  • Pinch zoom replacing mouse wheel.
  • Long press or explicit UI button for right-click/cancel-equivalent actions.
  • Safe area handling for top/bottom UI.
  • ScrollRect feel and button target size on touch.

Checks:

  • Single-player start, select civ/force, generate map, move unit, attack, build/train, end turn, save/load, and finish game remain possible on touch.
  • Steam keyboard/mouse behavior is not removed.
  • UI text and buttons fit common iPhone and iPad aspect ratios.

Phase 4: YooAsset AssetBundle Flow

Goal: move toward AB/resource hot update without destabilizing the Steam build.

Use the existing YooAsset package and AssetBundleCollectorSetting.asset unless there is a clear reason not to.

Current TH1 built-in resource shape:

  • Packaged resource root is Assets/BundleResources.
  • TH1YooAssetBuildTools.ConfigureDefaultPackageCollector() should collect the whole root for the default package.
  • TH1AddressByBundleResourcesPath strips Assets/BundleResources/, so Assets/BundleResources/Export/Multilingual.asset becomes address Export/Multilingual.asset.
  • TH1Resource.ResourceLoader.Load<T>("Export/VersionConfig") and similar calls normalize addresses and try extension candidates. Do not change runtime callers to platform-specific paths when the collector/address rule is the real issue.
  • Runtime config/localization/table data usually reads Export/...; raw DataAssets/... is the authoring source copied/transformed by the multilingual export/import flow.

Recommended sequence:

  1. Finish a real ResourceManager wrapper around YooAsset package initialization.
  2. Start with OfflinePlayMode so resources are packaged with the app.
  3. Add platform-specific package build output: Standalone and iOS must produce separate bundles.
  4. Move high-value resource groups first: UI prefabs, common sprites, audio, config data assets, large visual assets.
  5. Replace Resources.Load through local resource/cache entrypoints, not scattered direct YooAsset calls.
  6. Only after Offline is stable, add HostPlayMode for CDN/OSS remote resource update.

Resource rules:

  • Keep stable logical addresses. Do not make gameplay code depend on platform-specific paths.
  • Do not modify export-flow outputs such as Unity/Assets/BundleResources/Export/*, Tools/Multilingual.xlsx, or Tools/MultilingualTxt.txt unless the user asked for export/import changes.
  • In the current migration, export-flow outputs live under Assets/BundleResources/Export/*. If a package shows the wrong version, stale text, or stale table config, first check whether DataAssets -> Export was refreshed before AB build.
  • Before a release/debug package, run or explicitly skip with confirmation the multilingual export/import flow (Tools/一键导出导回 or the checkbox in TH1UnifiedBuildWindow). It refreshes Export/*, Multilingual.asset, Excel/TXT intermediate files, and MatchLevelData/ExportLevelData.bytes.
  • Use iOS-specific texture/audio compression settings and bundle output. Do not reuse PC texture assumptions blindly.
  • Treat generated config/DataAsset loading as compatibility-sensitive; preserve existing table and localization behavior.
  • For Unity 2021+ / Unity 2022 with YooAsset 2.1.1, prefer SBP (ScriptableBuildPipeline) for built-in package builds. YooAsset's BBP path uses Unity's old BuildPipeline.BuildAssetBundles call and can trigger the internal assertion m_InstanceIDToAssetBundleIndex.count(id) > 0.
  • YooAsset 2.1.1 SBP does not support ForceRebuild; use IncrementalBuild and keep BuildinFileCopyOption = ClearAndCopyAll when building TH1 built-in packages.
  • Switching BBP to SBP is not a fix for missing HybridCLR AOT metadata. Diagnose HybridCLR installer/generate failures separately.
  • Do not mix BBP and SBP outputs within one remote patch diff chain. Treat a pipeline switch as a full new package baseline and verify resource loading before release.

Checks:

  • Steam package can still load existing resources in editor and standalone.
  • iOS package uses iOS bundles and does not load PC-only bundle variants.
  • Missing asset failures are logged with package/address/platform.
  • No synchronous remote download is required during deterministic action execution.
  • After a BBP/SBP pipeline change, smoke-test main menu, config loading, UI opening, entering a game, map/unit rendering, and audio/effect loading.

Phase 5: Build Pipeline And Release Discipline

Maintain one main branch with separate build profiles:

  • Steam build: Standalone, STEAM_CHANNEL, Steamworks.NET, Steam lobby/P2P, Workshop, Steam auth/upload.
  • iOS build: iOS, IL2CPP, mobile platform services, no Steamworks compile dependency, touch enabled, iOS bundles.
  • Hot update build: platform-specific HybridCLR DLL/AOT metadata and YooAsset manifests.

Prefer Tools/TH1/一体化出包工具 for manual PC/iOS packaging. The one-click path should be staged and stop on first failure:

  1. Apply version/platform/package defines and OPS obfuscation target.
  2. Optional but recommended multilingual export/import (DataAssets -> Export).
  3. Configure HybridCLR.
  4. Configure YooAsset collector.
  5. HybridCLR GenerateAll.
  6. Build hotfix DLL/AOT metadata.
  7. Build YooAsset built-in AB.
  8. Check build blockers.
  9. Build Player.

The confirmation dialog should show the selected version and whether the multilingual export/import step is enabled.

Release branches may exist for stabilization only:

  • release/steam-* and release/ios-* may freeze and cherry-pick fixes.
  • All durable business logic fixes should return to the shared mainline.
  • Avoid copy-pasted Steam/mobile variants of gameplay classes.

Verification

For ordinary C# changes run:

dotnet build Unity/Assembly-CSharp.csproj --no-restore

For editor/build pipeline/resource tooling changes also run:

dotnet build Unity/Assembly-CSharp-Editor.csproj --no-restore

Also verify the relevant Unity builds when possible:

  • Steam Editor/Standalone smoke test.
  • iOS switch-platform compile or Xcode export.
  • HybridCLR generate/compile hotfix artifacts.
  • YooAsset build for the touched package/platform.

Manual smoke checklist:

  • Steam: start game, create/join lobby if network touched, Workshop/mod page if Workshop touched, bug report/upload if backend touched.
  • iOS/mobile: launch, start single-player, touch select/move/action/end turn, save/load, rotate/aspect/safe area check.
  • Shared: config load/save, localization, achievements/local progress, no hardcoded game-facing text added.

Stop Conditions

Stop and ask before continuing if:

  • A change would require editing MemoryPack serialization compatibility, obfuscation config, generated config outputs, or Steam backend auth contracts.
  • The only easy path is duplicating gameplay code between Steam and iOS.
  • iOS support would require removing or weakening existing Steam multiplayer guarantees.
  • A hotfix assembly needs to own authoritative action/network/replay logic before the migration baseline is stable.