Phase 1: Bootstrap (nothing else works without this)
GET getlsu3dversion.php, extractresMsg(RSA pubkey) and the answering host.- Implement the GSL RSA-PKCS1v15 + AES-256-ECB-PKCS7 envelope (Gate-server RSA+AES handshake), Go has no stdlib ECB mode, write the trivial 16-byte-block loop.
POST getserverlist.php, parseserverList[], pick a server, persistat/rttokens.- Open a plain TCP socket to
ip:port. Implement the packet envelope, the length-derived XOR “encryption,” zlib compression, and theSFSObjectcodec (SFS2X wire protocol). - Send SFS
Loginwith the ~50-field parameter object (Identity, login & session), including theSecurityCode/OneCode/CoreVMD5 constructions. - Wait for
init.before→init→init.after. CapturechatTokenif chat is needed later. - Start a 4-second
PingPongRequestheartbeat.
Phase 2: Core loop
Build the command dispatch table directly from the extractedcmd-data catalog (see Command reference for the row schema and how to regenerate it, don’t hand-transcribe the ~3,178 rows by hand). Implement, in order: City/Building → Military/Army (the base-building + troop loop) → World Map (needed for march/PVE) → Alliance + Chat + Mail (high command-count payoff) → Hero → Shop/Economy.
Phase 3: Defer
Season (754 cmds) and Activity (900 cmds) are recurring/time-limited live-ops content, 54% of the entire protocol surface by command count, and it churns every patch. Implement only if a specific feature is explicitly wanted; treating either as a monolith is not advisable given how fragmented each sub-feature’s schema is.Crypto primitives checklist
Running the Go client
The roadmap above was actually built, the repository root is a from-scratch Go reimplementation of this protocol, live-tested against production (see Live validation against production for the full writeup of what’s confirmed against live production servers). It’s fully working and live-confirmed for GSL crypto, SFS2X packet framing (including Zstandard decompression), the SFSObject codec, brand-new-guest-account login, email-verification account binding, resource collection across 13 confirmed building types (Farmland, Iron Mine, Gold Mine, Smelter, Material Workshop, Training Base, Oil Well, Drone Parts Workshop, Component Factory, and the four Season 6 Spore Factory tiers, 7 more building types are wired in but still unconfirmed, see the building-type table), and a growing set of account-level automations, none of which are building-uuid-scoped: the “Armed Truck”/“Overlord” idle rewards (lw.pve.idle.reward), greeting city visitors (visitor.operate), bulk-helping alliance members (al.help.all), claiming all alliance gifts (alliance.reward.allreceive), claiming all mail (chat.get.system.mails + mail.reward.batch), donating to the alliance’s currently-recommended tech (science.data.refresh + al.science.donate), and both once-a-day VIP claims (vip.add.login.score, vip.get.every.day.reward), see Live validation against production for the full writeup of each. Reconnecting into an established real account’s live game state was proven working end-to-end when first captured; that capture used the ta analytics blob’s real device/anti-fraud sub-fields, which a later security fix replaced with empty-string placeholders, and reconnect is now re-confirmed still working with those placeholders (an unattended cron reconnected and collected real resources over multiple days, August 2026). What remains open is the minimal required ta content and a fully from-scratch login without a captured token; see Live validation against production for the current state.
Not yet general-purpose. The reconnect path currently needs a session config captured from a real client login (see below) rather than deriving one from scratch. A from-scratch login using
-cs-ios-equivalent identity from the very first GSL call hasn’t been tried yet.-collect run.
1
Build
2
Set up a session config (recommended)
Reconnecting into an established account needs several values that can only come from a real client’s own login (device ID, access token, ShuMei fingerprint, …). Rather than typing them on the command line every time, put them in a JSON file:Where these values come from: capture a real login (e.g.
~/.lastwar_goclient_session.json is auto-loaded on every run if present, no flag needed. To use a different file, pass -config /path/to/file.json. Individual -cs-* flags still override whatever the config file says, for one-off tests.tcpdump while the real app logs in, since the SFS2X game socket is plain TCP with no TLS) and decode the Login request, see Capturing and decoding traffic for the exact, reproducible pipeline. gameUid/ip/port/zone also show up in a GSL getserverlist response’s serverList[] entries. The access token is not single-use, but it is bound to the platform identity (iosMode) it was issued under, and it will eventually need refreshing from a fresh capture.3
Run a collection pass
Other ways to run it
~/.lastwar_goclient_* (deviceId, username, gameUid, loginKey) independent of the session config, so repeated guest/email-flow runs present a consistent device to the server. Delete those files to start fully fresh.
Flags reference
The definitive, always-current flag list lives in the code itself, run./lastwar-client -h,
or read the flag definitions directly in main.go. A duplicated table here has already drifted
out of sync with the real flags more than once; the root README.md’s Usage section covers the
common cases with a live-tested example for each.
Project layout
See theREADME.md’s “Project layout” section at the repository root for the current package map
(internal/{sfs,crypto,gsl,session,game,auth,app} plus the cmd/lastwar-client entry point),
keeping a second copy here is exactly what caused it to drift out of sync.