Manual/Transport & Bootstrap/Identity, login & session
v1.0.364 notes · build 2026-11-07 12:10 UTC

Identity, login & session

~50-field Login · token lifecycle · error codes · init push
#auth #session

Device identity

deviceId = PM_getDeviceUDID() (Android ID or GAID) else SystemInfo.deviceUniqueIdentifier + rand; suffix _3d (debug) / _n3d; airKey = "lwDid_" + base64(utf8(deviceId)); persisted PlayerPrefs["DEVICE_ID"], iOS Keychain mirror (survives uninstall). assets/deviceId.txt contains "1", read by no code.

SFS2X Login

LoginRequest (action 1), password always empty; the real auth rides ~50 parameters fields:

  • gameUid (also the SFS username), serverId = zone minus "APS" prefix, at (if non-empty)
  • appVersion/versionCode (Android 1.0.364/1892, iOS 1.0.344/786; the token is bound to packageName/platform/appVersion/versionCode live)
  • SecurityCode = MD5(cmdBaseTime + "4d1c383ccbedf3d98320d6ea06d8dedc" + gameUid)
  • OneCode = char-interleave of MD5(rand32) and rand32; CoreV = interleave of MD5(reverse(base64(rand32))) then MD5(that + rand32)
  • packageSign = SHA1(packageName); psh: Android MD5(cmdBaseTime + hex(signing-cert DER)) (SdkManager.LW_PSH()); iOS MD5(cmdBaseTime + "7" + "com.lastwar.ios") ("3" when embedded.mobileprovision is present static); accepted live 2026-10-18
  • shumeiBoxId (empty = first-class path, switch 25), dataConfigMd5 (server-issued echo), resVersion, plus ~15 device/ad ids (androidDid, IMEI, gaid, afuid, firebaseId)

Tokens (at/rt)

Come only from getserverlist.php. PlayerPrefs: Login.access_token/_time, Login.refresh_token/_time (times = issue moment, unix s live), Login.login_key (one-shot, cleared after each GSL), Setting.GAME_UID ← init.user.uid, SERVER_IP/SERVER_PORT/SERVER_ZONE/SERVER_CONNECTION_TYPE.

  • GSL opt logic: loginKey≠"" → opt=login+loginKey; gameuid="" → opt=new; else stale = attime > 0 && now − attime >= 2592000 (30 d); rt="" → opt=fix; at=""||stale → opt=refresh+rt; else no opt.
  • GSL result codes: 0 success (empty serverList → E120); 201 → E116_201 + quit; 211 clears at/rt/loginKey; 212 also clears server info + gameUid; 213 → E213 ERROR_CHECK_SERVER_LIST_ENCRYPT; other non-zero → E116.

Login rejections (SFS ec=28, reason in ep[0])

E001/E002 account-not-exist; E005 maintenance (also wrong-platform token, or Android ta = {} live); E011 ERROR_LOGIN_ACCESS_TOKEN — clears only at, keeps rt → reload runs opt=refresh live; E012 Korean real-name; E013–E016 season bans/maintenance; E014 ERROR_OLD_UID_USE_NEW_RT clears all; 4;… ban; 43/44 IP/device blacklist → E117; 45–48 account deletion; 49 TRAFFIC_CONTROL_BANNED_USER; 55–58 role deletion (new in 1.0.364). Ban ep[0] = one ;-separated string 4;<time>;<type>;<reason>;<roleName>; negative time = permanent (9223372036854775806 = all-time); named reasons observed only 705 Data Error, 706 Exploitation of Glitches, 727 Speech Rules, 602 character deletion (others display "Data Error"). E1xx (E101–E117) are client-local, never server-sent.

  • serverInfo redirect in the Login response: save ip/port/zone/uid, reconnect straight to ConnectGame — no GSL, same at; serverInfo.uid replaces gameUid live.

Own-device / account binding live

  1. guest login; 2. email verification (account.login.send.verify.code; a repeat within ~2 min sends no email, ~12 min does; a spent code → errorCode=208193); 3. push.account.login.new returns gameUid + loginKey (top level; accountArr[] role entries carry no loginKey); 4. GSL opt=login → the device's own at/rt.
  • Established-account Android Login with ta = {} → E005; a filled ta (#os=Android, #bundle_id=com.fun.lastwar.gp, lw_platform=market_global, build in #app_version/lw_version/lw_buildcode, lw_zone) is accepted; guests accept either. iOS fill: #os=iOS, #bundle_id=com.lastwar.ios, lw_platform=AppStore; lw_device_id/lw_shumei_id/lw_airKey blank. A failed bind leaves a guest that later gets E011. opt=refresh at 8 days returns the current at with the original at.time, no rt (no rotation) live.

Post-login sequence live

  • Server pushes bare init (~287 KB, 247 top-level keys) ~200 ms after Login.
  • Client sends change.user.parseid and check.device.change {_id} (reply r:false, ignored), ~45 reads from Lua RequestOtherLoginMsg (~224 extension requests in the first 5 s), login.ext {info, add} (init.add {a,b,data,enc}: hardcoded-AES-decrypt data, execute as Lua via DoString, return the result — integrity challenge), nine mail.delete.batch {type:"auto", uids} at 4.7 s; push.shumei.id {shumeiId} at 0.5 s, cached and reported as ta.lw_shumei_id.
  • init fields: user (59 keys: uid, serverId, deviceBindTimes, gold/gold1/paidGold = total/free/paid diamonds; holds the account email — never log raw), building_new[], visitor (addNum,maxNum,list[] of {uid,eventId,startTime,visitorId,type,extendInfo}), vip.vipInfo, tomorrow (next server-day boundary; real_timezone_offset=-7200 → day = 02:00 UTC live), mixedInfo (todayLoginTimes,scoreSwitch,materialBackup,popRate), freeRewardArr, todayFreeStamina, queue_new[], collect_reward[], allianceNewMail, dataConfig (k1–k30, replaced wholesale), chatToken, add.
  • Day zero = now − (now + offset·1000) mod 86,400,000 ms; OnUnDelayPassDay at the boundary, OnPassDay 5–15 s later (61 DataCenter managers refetch), OnPassWeek 4 s after Monday; DST never moves the boundary.

Visitor types static

init.visitor.list[].type = queue type (0 GEN_BY_TIME, 1 CYCLE_REWARD, 2 STAGE, 4 WORKER_LOTTERY, 5 DOMINATOR, 6 AllianceCongratulation, 8 SystemGift, 11 ActivityVisitor, 12 AllianceInvite, 18 S0_ALLIANCE_BOSS, 19 SURVIVOR_PACK_GIFT, 20 ProtectCoverVisitor); behaviour = lw_base_visitor_event[eventId].type (1 MERCHANT, 2 GIFT, 3 RECRUITMENT, 4 BATTLE, … 30 SystemGift). MERCHANT (events 1001/1002): visitor.operate {uid, operate} — 1 = buy, 0 = decline. Uids eventId + 100000000 (events 6001, 8001, 12001, S0-boss, ProtectCover) never reach the server. Across 326 live accepts, behaviour types were only 2, 3, 10 live.

Guest & social paths

account.login.new type=1 + deviceId/airKey/pf ("AppStore" iOS, "market_global" Android); social = Google type=2, Apple Game Center type=4, Play Games type=5. client_config switch 21 + no stored gameUid → connect to the GSL-named loginServer {ip, port, ws_ip, ws_port}; login.push.shumei.exception.level gates the New-game picker static. Chat is a separate JSON WebSocket authed by init.chatToken; discovery /server/links on lastwar-chat-* hosts (or chat_server_links.php); every message signed MD5(uid + json(sortedParams) + sendTime + connectionSign), fixed app id 100017; channel optional.

notes build 2026-11-07 12:10 UTC · lw-notes@9c47e1b · sources: own capture/analysis cross-checked against the public dossier