Identity, login & 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(Android1.0.364/1892, iOS1.0.344/786; the token is bound topackageName/platform/appVersion/versionCodelive)SecurityCode = MD5(cmdBaseTime + "4d1c383ccbedf3d98320d6ea06d8dedc" + gameUid)OneCode= char-interleave ofMD5(rand32)andrand32;CoreV= interleave ofMD5(reverse(base64(rand32)))thenMD5(that + rand32)packageSign = SHA1(packageName);psh: AndroidMD5(cmdBaseTime + hex(signing-cert DER))(SdkManager.LW_PSH()); iOSMD5(cmdBaseTime + "7" + "com.lastwar.ios")("3"whenembedded.mobileprovisionis present static); accepted live 2026-10-18shumeiBoxId(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
optlogic:loginKey≠"" →opt=login+loginKey;gameuid="" →opt=new; elsestale = attime > 0 && now − attime >= 2592000(30 d);rt="" →opt=fix;at=""||stale →opt=refresh+rt; else noopt. - GSL result codes:
0success (emptyserverList→E120);201→E116_201+ quit;211clearsat/rt/loginKey;212also clears server info +gameUid;213→E213ERROR_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.
serverInforedirect in the Login response: save ip/port/zone/uid, reconnect straight toConnectGame— no GSL, sameat;serverInfo.uidreplacesgameUidlive.
Own-device / account binding live
- 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.newreturnsgameUid+loginKey(top level;accountArr[]role entries carry nologinKey); 4. GSLopt=login→ the device's ownat/rt.
- Established-account Android
Loginwithta={}→E005; a filledta(#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_airKeyblank. A failed bind leaves a guest that later getsE011.opt=refreshat 8 days returns the currentatwith the originalat.time, nort(no rotation) live.
Post-login sequence live
- Server pushes bare
init(~287 KB, 247 top-level keys) ~200 ms afterLogin. - Client sends
change.user.parseidandcheck.device.change {_id}(replyr:false, ignored), ~45 reads from LuaRequestOtherLoginMsg(~224 extension requests in the first 5 s),login.ext {info, add}(init.add {a,b,data,enc}: hardcoded-AES-decryptdata, execute as Lua viaDoString, return the result — integrity challenge), ninemail.delete.batch {type:"auto", uids}at 4.7 s;push.shumei.id {shumeiId}at 0.5 s, cached and reported asta.lw_shumei_id. initfields: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,000ms;OnUnDelayPassDayat the boundary,OnPassDay5–15 s later (61 DataCenter managers refetch),OnPassWeek4 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.