Status at a glance
This table exists so a future update can change just the relevant row without rewriting the rest of the page. Every item below is resolved as originally framed, there are no genuinely open items remaining across any of the 10 rows here. One row (established-account reconnect block) grew a narrower follow-on question of its own, whether reconnect still worked after a security fix swapped part of theta blob for empty placeholders, and that follow-on is now also resolved (confirmed working against the shipped code, August 2026); the caveat below is kept for the record and for the residual, even-narrower items (minimal ta content, fully from-scratch login). If a future finding reopens one of these outright, update its row and status here rather than adding a separate list.
Resolved by live testing
See Live validation against production for full methodology.Enum numeric values (: Resolved the authoritativeBuildingTypes, …)BuildingTypestable lives inGlobal_EnumType.lua, giving every building’s internal codename and numeric ID directly. See the Game entity ID reference for the full extraction across buildings/monsters/units/heroes/resources/items.Whether the server enforces ShuMei/ACE anti-cheat data on login: Resolved yes, but conditionally: enforced (and reproducibly blocking) when reconnecting to an account with real history; not enforced at all for new-guest-account creation. Full test matrix in Live validation against production.The missing post-login: Resolved none of the three previously-identified leads were the cause. The real bug:initpushpacket.go’sReadPacketalready correctly detected the server’s Zstandard-compressed packets (header bits0x20compressed +0x10useLZ4-repurposed-as-Zstd) but decompression itself was never implemented, it just returned an error, which every read loop indistinguishably treated as “connection closed.” Fixed by adding a real Zstd decoder (github.com/klauspost/compress/zstd, a shared lazily-initializedzstd.Decoder) in that same branch. The post-login init push turned out to be ~313KB of Zstd-compressedSFSObjectdata on the wire; decoding it surfaced two further binary-codec bugs insfsobject.go:LONG_ARRAY/FLOAT_ARRAY/DOUBLE_ARRAY/BOOL_ARRAY/SHORT_ARRAY(tags 9, 11, 13, 14, 15) were simply unimplemented, andBYTE_ARRAY(tag 10) was decoded with the wrong length-prefix width (i16 instead of the real i32), silently corrupting decode of any packet containing one, see the corrected wire-format table in SFS2X wire protocol. Once both were fixed, the init push arrived cleanly on the very first attempt: no reconnect loop, no extended timeout needed, and the active-pulllogin.initfallback never had to fire. Of the two mechanisms built earlier to chase this symptom, only the three-attempt reconnect-on-timeout loop was unnecessary and has been removed (max login attempts back to 1); the halfway active-pulllogin.initsend insidewaitForInitPushwas kept in place, unconditionally, as a real, still-exercised fallback for a genuinely silent server, not reverted, see Live validation against production for the full mechanism split.The established-account reconnect block: Resolved not anti-fraud, not device fingerprinting, not session locking. Captured live traffic from the real iOS/Mac-Catalyst app (/Applications/Last War.app) viatcpdumpagainst the same account, decoded the Login packet field by field (the SFS2X game socket is plain TCP, no TLS), and confirmed by a byte-for-byte raw replay that the same access token works cleanly across repeated connections, it is not single-use, and there is no rate-limit or already-connected mechanism. The actual gate: a token is bound to the exactpackageName/platform/appVersion/versionCodeidentity it was issued under, andgoclienthardcoded an Android identity unconditionally even when replaying an iOS-issued token. Fixed inidentity.go(BuildLoginParamsgained anIOSModeswitch coveringpackageName,packageSign, confirmed stillSHA1(packageName)on iOS too,platform,pf,appVersion,versionCode, the iOS-onlyidfa/idfv/phone_native_screenfields, removal of Android-only fields, and, at the time, populatingtawith real captured content instead of an empty placeholder), pluscrossserver.goand new-cs-ios/-cs-atflags inmain.go. A real reconnect through the Go client’s own from-scratch SFS2X implementation then succeeded cleanly: Login OK, followed immediately by a correctly Zstd-decoded init push showing the account’s 162 real buildings. Caveat: the exact minimal required content oftawasn’t isolated, only that the full real captured blob worked, at the time of that capture. A later security fix replacedta’sLwDeviceID/LwShumeiID/LwAirKeysub-fields with empty-string placeholders (they were leaking the same live device secrets in cleartext inside an opaque JSON string); reconnect is now re-confirmed working with those placeholders against the shipped code, an unattended cron running this client reconnected and collected real resources over multiple days (August 2026), re-verified after a live zone-server migration, so the server does not requireta’s real device sub-fields. What’s left is even narrower: the minimal requiredtacontent still isn’t isolated (a different token needs its own freshtaregardless, since session-specific fields live inside it), and a fully from-scratch login without a captured token hasn’t been tried, see Live validation against production.
Resolved by a dedicated research pass
Full findings, with file:line citations into the client source, follow below.March “recall home” encoding: Resolved not a coordinate sentinel, it’s the literal enum constantMarchTargetType.BACK_HOME = 3(Global_EnumType.lua:3945), sent in the ordinarytargetfield ofworld.march.change(the same command used to redirect a march anywhere), withpathset back to the player’s home-city tile. Full call chain traced from the UI button through to the wire request.: Resolved the “no confirmed call site” framing was outdated:WorldPointInfo.proto’s actual transportworld.get.newandworld.get.block’s response fieldpoints(an array of raw byte blobs) is decoded viaWorldPointInfo.Parser.ParseFrominWorldPointManager.ParseWorldGetBlock, feeding the ~30 concrete point-type classes that render the world map. A second, nested call site decodes march destination points the same way.Four unrecoverable proto schemas: Resolved all four (ScoreProgressInfo,WorldCityRankMail,LwScoutReport,ShieldInfo) were fully recoverable, and not where prior analysis stopped looking, see the callout below.: Resolved the “no call site” claim was a tooling false-negative (this environment’slibxlua.so’sCalcChatSign/AES/MD5 helpersgrepisugrep, which silently skips binary files without-a). The real call site was already sitting in report 06 (ChatNetManager:InitWebSocketInst, an alternate native-transport branch). Direct ARM64 disassembly of the native function (it ships unstripped, with symbols) confirms it’s exactlyMD5(MD5(appId+uid) + timestamp), plain, unkeyed, textbook MD5, byte-identical to the already-known C# formula. Proprietary-crypto risk downgraded from “unknown” to confirmed none.: Resolved not a hash or local computation, it’s a single server-pushed integer,PlayerInfo:GetABTestTableNamemonopoly_ab, delivered in the post-loginInitpayload’suserobject. Each value (1, 2, 3, 5, 6, 7, 8, 9, 10, 13, 16, 18 in this build) is a reserved slot for one named A/B test; the function’sIs<TestName>B()checks compare against it directly. A reimplementation cannot derive this offline, it must be read from a real login response.Season vs. Activity domain boundary: Resolved (accuracy-measured, not just re-asserted): a genuine random sample of 100 commands (50 Season, 50 Activity) checked against actual handler code came back 97% label-defensible. The 3 confirmed misclassifications aren’t random noise, they’re all cases where a single feature’s sibling commands got torn across both domains because the classifier operates per-command/per-folder rather than per-DataCenter-manager (e.g. the Desert Storm fishing minigame’s 8 commands split ~50/50 because one cmd string happened to contain"fish.pond", a substring the classifier’s ordered rule list catches before the generic"season"fallback).
Bonus finding: 18 proto schemas recovered, not just 4. The “unrecoverable” verdict on the four proto schemas stopped at “not in the Lua bundle” without checking two other places they actually live. Three of the four (
ScoreProgressInfo, ShieldInfo, LwScoutReport) turned out to have full Google.Protobuf-generated C# classes in Assembly-CSharp.decompiled.cs, each embedding its complete compiled FileDescriptorProto as a base64 blob. All four, WorldCityRankMail included, which has zero C# presence at all, are also physically shipped as Unity TextAssets inside a custom-packed AssetBundle blob (assets/AssetBundles/BundleFragment0.bytes, 525MB, indexed by a name→offset table also in the APK). Locating and extracting that bundle’s gameres_main_dir_proto entry recovered all 18 .proto.bytes files the client ships (not just the 4 originally sought) as serialized FileDescriptorSet binaries, decoded to human-readable .proto text and cross-validated field-for-field against the independently-recovered C# descriptors. Both the raw binaries and decoded schemas are saved at extracted/recovered_protos/*.proto / *.decoded.txt.