Alliance, chat & mail
Two chat channels
- SFS socket (main) carries moderation (ban/lock), room admin, "share to chat" attachments/cards, player search — binary SFSObject, main-session auth.
- Dedicated chat WebSocket carries message text, room join, history/pagination, read receipts — JSON text frames, auth via
chatTokenfrom the SFSinitpush + signed per-connection header. Sending text =cmd="chat.room"on the WebSocket; SFSal.msg/chat.countryonly share rich attachments, no free text. - Frame signing:
sign = MD5(uid + json(sortedRecursively(params)) + sendTime + connectionSign); frame{cmd, params, sendTime, sign}.
Room ids (1.0.364)
alliance alliance_<sourceServerId>_<allianceId>; R4/R5 room alliance_manager_<sid>_<aid>; notice alliance_<sid>_<aid>_Notice. Joining gated by room_join_control DataConfig: default client-side room.joinMulti {rooms:[{id, group:"alliance"}]}; with the switch on, the client sends chat.permission.syn {action} (actions joinAlliance, leaveAlliance, rankChange, notCheck) on SFS; server replies push.room.synPermission {roomId, auth, group}. common.chat.room.id returns allRooms[{group, roomId}], only when ClientSwitch.CHAT_SERVER_ROOMLIST is on.
WebSocket transport [static — 1.0.364 Lua ChatNetManager/ChatManager2, C# ChatService]
- Discovery: HTTPS form POST to the first reachable of
https://lastwar-chat-cf.lastwarapp.net/server/links,https://lastwar-chat-us-aws-ali.lastwargame.com/server/links,https://lastwar-chat-us-gcp-ali.lastwargame.com/server/links; withCheckChatNewLineon, posts to/gameservice/chat_server_links.phponlastwar-serverlist-*hosts. Form fields:t(server time s),s(MD5 sig),a(app id),u(uid),serverId,f=cnin China. Response{code, data:[{protocol, ip, port}], data_new};ChatUseNewLineselectsdata_new. Retries every 3 s rotating URLs. - Connect
{protocol}://{ip}:{port}no path; headersAPPID,TIME(ms),UID,SIGN(=connectionSign),CHATTOKEN. - Keepalive: ping 3 s; close after 9 s silence; server sends literal
heartbeattext frames. - After
login.success:user.setInfo,user.setSwitchFlag,room.getCustomRoomListV2,room.joinMulti, thenhistory.roomsv2per room. another.login= same uid elsewhere; client closes, no reconnect. Replies reuse the requestcmd+result; pushes carrydata. Red packets/gifts/rewards are claimed on the game socket; chat delivers ids only.
Alliance commands
- Lifecycle
al.create/al.dismiss/al.leave; joinal.apply/al.acceptapply/al.refuseapply(open alliances auto-join);al.invite/al.acceptinvite(via mail);al.kick/al.setrank(kicks rate-limited);alliance.declare.war.create; rally-assistal.call.help/al.show.help. al.help.alllive — bulk-completes pending help; only wire fieldcmdBaseTime(Long, epoch-ms); no-op success when nothing pending.- Gifts
alliance.reward.list/alliance.reward.allreceivelive — claim-all pertype(1= Premium,2= Regular); Premium claim-all unlocked at alliance gift level ≥alliance_giftconfigk5. - Tech:
science.data.refreshlive — full tech tree, no params; exactly one entrystate=1(the "Recommended" tech).al.science.donatelive costs resource;al.science.refreshNum {scienceId}live →maxNum=30,refreshTimeBlock=1200000;errorCode=120471("Donate science CD time is not finish") = the charge pool is empty, not a per-donation CD.al.science.donate.goldcosts gems, no charge limit (useGoldNum/maxGoldNumboth999999999); no batch field — "hold to donate" is client-side auto-repeat.al.science.recommend— officer marks/unmarksscienceIdRecommended; read viastate.
mail.read {uid: mailId, type, toUser: senderUid}.chat.get.system.mails— paginated (clientseq/time/count/firstCmd/isAllin;more/lastUid/lastMailTimeout); response is a separate pushpush.chat.get.system.mailslive cold start (time:0, emptyclientseq) paginated a 350-entry mailbox across 4 pages; push gaineddelActivityIdsin 1.0.364.mail.delete.batch {type:"auto", uids}sent at login; auto-delete removes >30-day mail once daily; answered viapush.mail.delete.batch.mail.read.status.betchlive — marks read; needs onlyuids(comma-separated string GUIDs), global across categories, not gated on reward status. Skip types 112/134/135 (translation-rating rewards), saved mail (saveFlag != 0), ungrouped mail; hide-group mail (arena, truck battle reports) marked read, never claimed.mail.reward/.batchlive: uids are string GUIDs (not int64); batch claim scoped per mailbox categorytype.mail.reward.batchtypevalues (internal group ids, UtfString):2Report,3Alliance,4Activity,5System,6Daily,7Charge,8Favor (alsosaveFlag==1),9Season. Mapping viaMailTypeToInternalGroup: 140 rows inMailEnum.lua1.0.367 +mail_grouprows (MailDataManager.lua:30-36); 144 types with table 39879. Claim All walks groups2, 3, 4, 9, 5, 6, 7, 8(MailShowGroup); ungrouped mail is never read/claimed; hide group1is marked read at login, never bulk-claimed. Sending raw mailtypeas Int is also accepted live ({success=true}).mail.send(1:1 or alliance-wide);get.fight.report.detail.
Government ("Kingdom")
Cross-alliance elected leadership: choose.king, kingdom.position.appoint/.apply/.resign, cross.throne.* (14 commands, cross-server king-of-the-hill) — ordinary SFS commands, no separate channel.
notes build 2026-11-07 12:10 UTC · lw-notes@9c47e1b · sources: own capture/analysis cross-checked against the public dossier