feat(state): 迁移 system/contextTutorial/battleTutorial/spectator/twoStepVerificationState/antiAddiction/moderation/migrationState/register/userReportsState

This commit is contained in:
TermiNexus committed 2026-10-24 15:32:47 +08:00
1 parent 8633fc91c1
commit af005ab019
18 files changed
+2030 -95

No files matched your search

+38 -6
View File
@@ -143,7 +143,7 @@
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
- 已迁移切片 **43/89**:
- 已迁移切片 **53/89**:
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
@@ -279,12 +279,42 @@
新增枚举 `RewardVideoDisplayState { NONE, LOADING, DISPLAY }`,缺省 `NONE`;`SetDisplayState` 整体替换。
- `passwordChangeState` → `VD` / `uu.pr6`(`PasswordChangeReducer.kt`):3 个字符串字段(hash / uid / email),
缺省全为空串;`SetPasswordChangeHash` 只写 hash,`ShowEmailAndPasswordChangeDialog` 写 uid/email 并保留 hash。
- `system` → `_9` / `w9().xt9`(`SystemReducer.kt`):3 字段,2 个分支。
`DoShowMessage(message, restart)` 写前两字段,`SetFatalError` 只写 `fatalError`。
- `contextTutorial` → `Mst` / `Jst().tth`(`ContextTutorialReducer.kt`):4 字段,5 个分支。
`MarkElementAsViewed` 从 `unViewedElements` 删一个元素(`Ym`),其余为整表 / 整 Map 替换。
- `battleTutorial` → `rE` / `Q.sq8`(`BattleTutorialReducer.kt`):3 字段,4 个分支。
`oE`→`MarkMovementStarted` 置 `movementHasStarted`,`sE`→`ResetBattleTutorial` 整片复位。
- `spectator` → `HA` / `Li.jqm`(`SpectatorReducer.kt`):4 字段,6 个分支。
`JA`→`AddSpectator` 追加 `SpectatorInfo`(复用生成物里的同名模型),`ZA`→`RemoveSpectator` 按 userId 过滤,
`XA`→`SetSpectators` 整表替换,`YA`→`SetSpectatorsListVisible`。
`spectatorHudLayout` 的原模型是 15 字段的 HUD 布局对象(战斗层),按 `Any?` 收口,缺省 null。
- `twoStepVerificationState` → `x9` / `_f.atb`(`TwoStepVerificationReducer.kt`):6 字段,5 个分支。
`VerificationStatus` 三态:null 清空、false 写失败、true 直接把 `isActivated2FA` 置 true(原 `rta(cd, !0)`)。
- `antiAddiction` → `UC` / `W.iq8`(`AntiAddictionReducer.kt`):8 字段,5 个分支。
`FC`→`EnableRealNameCheck`、`XC`→`RequireUserIdVerification`(同时置 `unknownUserId`)、
`YC`→`UpdatePlayingTime`;`SetRealNameAndIdNumber` 的参数为 null 时保留原值。
- `moderation` → `NI` / `Ei.oqh`(`ModerationReducer.kt`):11 字段,13 个分支。
选中用户列表用 `ModerationUserRecord` 收口:`GI`/`WI` 先按 userId 去重再追加,`KI` 删除,`YI`/`uA` 清空;
`XI`/`JI` 与 `ZI`/`QI` 分别是战斗信息面板、系统消息的显示/收起,`tA`/`nA` 是封禁菜单。
`battleInfo` 的原模型 `MI` 有 13 字段(战斗层),按 `Any?` 收口。
- `migrationState` → `$it` / `Uv.rtg`(`MigrationStateReducer.kt`):7 字段,11 个分支。
新增 `MigrationLoginStatus { UNKNOWN, FREE, INCORRECT, BUSY, SHORT_NAME }` 与
`MigrationRegisterStatus { UNKNOWN, PASSWORD_CORRECT, PASSWORD_INCORRECT, FAILED }`(对应源码 `Eit()` / `Iit()`)。
`SetUid` / `ForceSetUid` / `SetPassword` 与注册、登录切片共用同一批动作类,因此几片 reducer 同时响应、各自动自己的字段。
- `register` → `Cit` / `cp.stg`(`RegisterReducer.kt`):16 字段,19 个分支。
新增 `UidStatus`、`RegistrationStatus` 两个枚举;`SetUid` / `ForceSetUid` 会复位 uid 校验状态,
且只有原状态不是 `INCORRECT` 时才把 `registerStatus` 一起打回 `UNKNOWN`(原 `2 === n.wtr_1.k3_1` 分支)。
- `userReportsState` → `Zst` / `qot.zth`(`UserReportsReducer.kt`):5 字段,6 个分支。
新增 `ReportKind { BATTLE_NAME, LOBBY_CHAT_MESSAGE, BATTLE_CHAT_MESSAGE }` 与 `ReportEntry(kind, targetId, expireTime)`;
三类举报都追加一条记录、有效期 +1 天。原 reducer 还把列表写回 localStorage 的 `SENT_REPORTS`,
属 storage port 职责,纯 reducer 不做 IO(Init 保留现有列表)。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 42 个有 reducer 的切片,
`launchParams` 与其余 **46 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 43/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **529 项全绿**,含 89 槽位完整性、42 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 52 个有 reducer 的切片,
`launchParams` 与其余 **36 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 53/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **627 项全绿**,含 89 槽位完整性、52 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
@@ -298,7 +328,9 @@
language 的 1 个、announcement 的 3 个、news 的 3 个(含两个单例)、notifications 的 2 个、
quests 的 1 个、lobbyLoader 的 2 个、purchaseDialogState 的 1 个、supportUrl 的 1 个、
notificationByCountry 的 1 个、mainQuest 的 1 个、rankUpDialogState 的 1 个、battleResult 的 3 个、
captchaState 的 5 个、modalScreen 的 1 个、droppableGold 的 2 个、creatorTagState 的 1 个匿名类 / 单例同样按效果定名并登记。
captchaState 的 5 个、modalScreen 的 1 个、droppableGold 的 2 个、creatorTagState 的 1 个、
battleTutorial 的 2 个、spectator 的 4 个、antiAddiction 的 3 个、moderation 的 13 个、
迁移 / 注册共用的 10 个(`Mit`…`_rt`)匿名类 / 单例同样按效果定名并登记。
### S4 引擎核心
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
+256 -54
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T06:30:01.615Z",
"generatedAt": "2026-10-24T07:23:39.141Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -49,7 +49,17 @@
"eventGoods",
"creatorTagState",
"rewardedVideosState",
"passwordChangeState"
"passwordChangeState",
"system",
"contextTutorial",
"battleTutorial",
"spectator",
"twoStepVerificationState",
"antiAddiction",
"moderation",
"migrationState",
"register",
"userReportsState"
],
"AnonymousActions": [
{
@@ -796,6 +806,198 @@
"Name": "ClearCreatorTag",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "creatorTag=null(`n.ar2(null)`)"
},
{
"Local": "oE",
"Name": "MarkMovementStarted",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "movementHasStarted=true(`n.pq8(cd, cd, !0)`)"
},
{
"Local": "sE",
"Name": "ResetBattleTutorial",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "new rE() 整片复位(`new rE()`)"
},
{
"Local": "XA",
"Name": "SetSpectators",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "spectators=整表(`n.yql(cd, cd, t.dqm_1)`)"
},
{
"Local": "JA",
"Name": "AddSpectator",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "spectators += `new iR(userId, uid, inSupportGroup, inPunishersGroup)`(`Hg`)"
},
{
"Local": "ZA",
"Name": "RemoveSpectator",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "spectators 按 userId 过滤掉一条(`Qd(s.kqm_1, t.iqm_1) || e.y(s)`)"
},
{
"Local": "YA",
"Name": "SetSpectatorsListVisible",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "spectatorsListVisible=布尔(`n.yql(cd, cd, cd, t.cqm_1)`)"
},
{
"Local": "FC",
"Name": "EnableRealNameCheck",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isEnableRealNameCheck=true(`n.lq7(!0)`)"
},
{
"Local": "XC",
"Name": "RequireUserIdVerification",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "unknownUserId=true、urlForEnterUserId=参数(`n.lq7(cd, !0, t.hq8_1)`)"
},
{
"Local": "YC",
"Name": "UpdatePlayingTime",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "playingTimeInMinutes、alertMessage(`n.lq7(cd, cd, cd, t.fq8_1, t.gq8_1)`)"
},
{
"Local": "UI",
"Name": "SetModeratorModes",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "moderatorMode / battleModeratorMode / eventOrganizerMode(`n.aqf(a, b, c)`)"
},
{
"Local": "FI",
"Name": "SetModerationUrlTemplates",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "两个 URL 模板(`n.aqf(cd, cd, cd, t.vqg_1, t.wqg_1)`)"
},
{
"Local": "XI",
"Name": "ShowBattleInfo",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "battleInfo + forceShowBattleInfo=true(`n.aqf(…, t.fqh_1, !0)`)"
},
{
"Local": "JI",
"Name": "HideBattleInfo",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "forceShowBattleInfo=false(`n.aqf(…, !1)`)"
},
{
"Local": "GI",
"Name": "AddSelectedUser",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "selectedUsers 去重后追加 `new OI(...)`(`Hg(e, new OI(...))`)"
},
{
"Local": "WI",
"Name": "AddSelectedUserFromChat",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "同 GI,另一个派发点(`Hg(_, new OI(...))`)"
},
{
"Local": "KI",
"Name": "RemoveSelectedUser",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "selectedUsers 按 userId 过滤(`Qd(v.oqf_1, t.eqh_1) || l.y(v)`)"
},
{
"Local": "YI",
"Name": "ClearSelectedUsers",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "selectedUsers 清空(`var p = md()`)"
},
{
"Local": "uA",
"Name": "ResetSelectedUsers",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "selectedUsers 清空(`var w = md()`,另一个派发点)"
},
{
"Local": "ZI",
"Name": "ShowModerationSystemMessage",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "systemMessage + forceShowSystemMessage=true(`n.aqf(…, t.gqh_1, !0)`)"
},
{
"Local": "QI",
"Name": "HideModerationSystemMessage",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "forceShowSystemMessage=false(`n.aqf(…, !1)`)"
},
{
"Local": "tA",
"Name": "ShowBlockUserMenu",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "forceShowBlockUserMenu=true(`n.aqf(…, !0)`)"
},
{
"Local": "nA",
"Name": "HideBlockUserMenu",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "forceShowBlockUserMenu=false(`n.aqf(…, !1)`)"
},
{
"Local": "Mit",
"Name": "DisableRegister",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isDisabled=true(`n.its(!0)`,只有注册切片读)"
},
{
"Local": "Dit",
"Name": "UidIsFree",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "loginStatus / uidValidationStatus = FREE(`lrt()`)"
},
{
"Local": "Lit",
"Name": "UidIsIncorrect",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "loginStatus / uidValidationStatus = INCORRECT(`frt()`)"
},
{
"Local": "Bit",
"Name": "ResetUidStatus",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "loginStatus / uidValidationStatus = UNKNOWN(`crt()`)"
},
{
"Local": "Hit",
"Name": "UidTooShort",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "loginStatus / uidValidationStatus = SHORT_NAME(`prt()`)"
},
{
"Local": "Uit",
"Name": "RegisterPasswordCorrect",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "registerStatus=PASSWORD_CORRECT(`(Iit(), Xv)`,只有注册切片读)"
},
{
"Local": "Fit",
"Name": "RegisterPasswordIncorrect",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "registerStatus=PASSWORD_INCORRECT(`drt()`)"
},
{
"Local": "Git",
"Name": "RegisterFailed",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "registerStatus=FAILED(`grt()`)"
},
{
"Local": "hrt",
"Name": "ResetRegisterStatus",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "registerStatus=UNKNOWN(`n.its(cd, cd, cd, cd, cd, wrt())`,只有注册切片读)"
},
{
"Local": "_rt",
"Name": "ResetAuthenticationForm",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "login=\"\"、两个状态复位(两个切片都读)"
}
],
"Markers": [
@@ -958,13 +1160,13 @@
"Reducer": "Q.sq8",
"Delegate": "Q.sq8(t, n.cpv_1)",
"Actions": [
"?oE",
"?sE",
"MarkMovementStarted",
"ResetBattleTutorial",
"SetFragsCount",
"ShowMessage"
],
"Default": "new rE()",
"Migrated": false
"Migrated": true
},
{
"Field": "garage",
@@ -1115,28 +1317,28 @@
"Reducer": "cp.stg",
"Delegate": "cp.stg(t, n.hpv_1)",
"Actions": [
"?Bit",
"?Dit",
"?Fit",
"?Git",
"?Hit",
"?Lit",
"?Mit",
"?Uit",
"?_rt",
"?hrt",
"DisableRegister",
"EnteredUidIsBusy",
"ForceSetUid",
"ReceiveOAuthUri",
"RegisterFailed",
"RegisterPasswordCorrect",
"RegisterPasswordIncorrect",
"ResetAuthenticationForm",
"ResetRegisterStatus",
"ResetUidStatus",
"SetOpenedPassword",
"SetPassword",
"SetPasswordRestrictions",
"SetUid",
"SocialNetworkAvailability",
"SocialNetworkClientIds"
"SocialNetworkClientIds",
"UidIsFree",
"UidIsIncorrect",
"UidTooShort"
],
"Default": "new Cit()",
"Migrated": false
"Migrated": true
},
{
"Field": "migrationState",
@@ -1145,20 +1347,20 @@
"Reducer": "Uv.rtg",
"Delegate": "Uv.rtg(t, n.ipv_1)",
"Actions": [
"?Bit",
"?Dit",
"?Fit",
"?Git",
"?Hit",
"?Lit",
"?_rt",
"EnteredUidIsBusy",
"ForceSetUid",
"RegisterFailed",
"RegisterPasswordIncorrect",
"ResetAuthenticationForm",
"ResetUidStatus",
"SetPassword",
"SetUid"
"SetUid",
"UidIsFree",
"UidIsIncorrect",
"UidTooShort"
],
"Default": "new $it()",
"Migrated": false
"Migrated": true
},
{
"Field": "inviteState",
@@ -1216,7 +1418,7 @@
"SetFatalError"
],
"Default": "new _9()",
"Migrated": false
"Migrated": true
},
{
"Field": "quests",
@@ -2178,14 +2380,14 @@
"Reducer": "W.iq8",
"Delegate": "W.iq8(t, n.kpx_1)",
"Actions": [
"?FC",
"?XC",
"?YC",
"EnableRealNameCheck",
"RequireUserIdVerification",
"SetRealNameAndIdNumber",
"SetValidateAntiAddictionUserDataStatus"
"SetValidateAntiAddictionUserDataStatus",
"UpdatePlayingTime"
],
"Default": "new UC()",
"Migrated": false
"Migrated": true
},
{
"Field": "spectator",
@@ -2194,15 +2396,15 @@
"Reducer": "Li.jqm",
"Delegate": "Li.jqm(t, n.lpx_1)",
"Actions": [
"?JA",
"?XA",
"?YA",
"?ZA",
"AddSpectator",
"RemoveSpectator",
"SetSpectator",
"SetSpectators",
"SetSpectatorsListVisible",
"UpdateHudLayout"
],
"Default": "new HA()",
"Migrated": false
"Migrated": true
},
{
"Field": "moderation",
@@ -2211,22 +2413,22 @@
"Reducer": "Ei.oqh",
"Delegate": "Ei.oqh(t, n.mpx_1)",
"Actions": [
"?FI",
"?GI",
"?JI",
"?KI",
"?QI",
"?UI",
"?WI",
"?XI",
"?YI",
"?ZI",
"?nA",
"?tA",
"?uA"
"AddSelectedUser",
"AddSelectedUserFromChat",
"ClearSelectedUsers",
"HideBattleInfo",
"HideBlockUserMenu",
"HideModerationSystemMessage",
"RemoveSelectedUser",
"ResetSelectedUsers",
"SetModerationUrlTemplates",
"SetModeratorModes",
"ShowBattleInfo",
"ShowBlockUserMenu",
"ShowModerationSystemMessage"
],
"Default": "new NI()",
"Migrated": false
"Migrated": true
},
{
"Field": "rulesUpdated",
@@ -2331,7 +2533,7 @@
"VerificationStatus"
],
"Default": "new x9()",
"Migrated": false
"Migrated": true
},
{
"Field": "rewardedVideosState",
@@ -2402,7 +2604,7 @@
"SetUnViewedElements"
],
"Default": "new Mst()",
"Migrated": false
"Migrated": true
},
{
"Field": "suppliesTutorial",
@@ -2472,7 +2674,7 @@
"SetAvailableReports"
],
"Default": "new Zst()",
"Migrated": false
"Migrated": true
},
{
"Field": "tankBuilderState",
+132
View File
@@ -200,6 +200,43 @@ const AnonymousActionNames = new Map([
['Yet', 'HideDroppableGoldAuthor'],
// creatorTagState 切片(qO / SO.er2):jO 没有 toString。
['jO', 'ClearCreatorTag'],
// battleTutorial 切片(rE / hE.sq8):oE/sE 没有 toString。
['oE', 'MarkMovementStarted'],
['sE', 'ResetBattleTutorial'],
// spectator 切片(HA / tR.jqm):XA/JA/ZA/YA 没有 toString。
['XA', 'SetSpectators'],
['JA', 'AddSpectator'],
['ZA', 'RemoveSpectator'],
['YA', 'SetSpectatorsListVisible'],
// antiAddiction 切片(UC / JC.iq8):FC/XC/YC 没有 toString。
['FC', 'EnableRealNameCheck'],
['XC', 'RequireUserIdVerification'],
['YC', 'UpdatePlayingTime'],
// moderation 切片(NI / _A.oqh):13 个类全都没有 toString。
['UI', 'SetModeratorModes'],
['FI', 'SetModerationUrlTemplates'],
['XI', 'ShowBattleInfo'],
['JI', 'HideBattleInfo'],
['GI', 'AddSelectedUser'],
['WI', 'AddSelectedUserFromChat'],
['KI', 'RemoveSelectedUser'],
['YI', 'ClearSelectedUsers'],
['uA', 'ResetSelectedUsers'],
['ZI', 'ShowModerationSystemMessage'],
['QI', 'HideModerationSystemMessage'],
['tA', 'ShowBlockUserMenu'],
['nA', 'HideBlockUserMenu'],
// 迁移 / 注册共用(module_10032 里各只定义一次):Mit/Dit/Lit/Bit/Uit/Fit/Git/Hit/hrt/_rt。
['Mit', 'DisableRegister'],
['Dit', 'UidIsFree'],
['Lit', 'UidIsIncorrect'],
['Bit', 'ResetUidStatus'],
['Hit', 'UidTooShort'],
['Uit', 'RegisterPasswordCorrect'],
['Fit', 'RegisterPasswordIncorrect'],
['Git', 'RegisterFailed'],
['hrt', 'ResetRegisterStatus'],
['_rt', 'ResetAuthenticationForm'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -628,6 +665,19 @@ const Migrated = {
creatorTagState: { Type: 'CreatorTagStateSlice', Reduce: 'ReduceCreatorTag' },
rewardedVideosState: { Type: 'RewardedVideosStateSlice', Reduce: 'ReduceRewardedVideos' },
passwordChangeState: { Type: 'PasswordChangeStateSlice', Reduce: 'ReducePasswordChange' },
system: { Type: 'SystemStateSlice', Reduce: 'ReduceSystem' },
contextTutorial: { Type: 'ContextTutorialStateSlice', Reduce: 'ReduceContextTutorial' },
battleTutorial: { Type: 'BattleTutorialStateSlice', Reduce: 'ReduceBattleTutorial' },
spectator: { Type: 'SpectatorStateSlice', Reduce: 'ReduceSpectator' },
twoStepVerificationState: {
Type: 'TwoStepVerificationStateSlice',
Reduce: 'ReduceTwoStepVerification',
},
antiAddiction: { Type: 'AntiAddictionStateSlice', Reduce: 'ReduceAntiAddiction' },
moderation: { Type: 'ModerationStateSlice', Reduce: 'ReduceModeration' },
migrationState: { Type: 'MigrationStateSlice', Reduce: 'ReduceMigrationState' },
register: { Type: 'RegisterStateSlice', Reduce: 'ReduceRegister' },
userReportsState: { Type: 'UserReportsStateSlice', Reduce: 'ReduceUserReports' },
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -877,6 +927,58 @@ const CreatorTagAnonymousEffects = {
jO: 'creatorTag=null(`n.ar2(null)`)',
};
// battleTutorial 切片(rE / hE.sq8):oE/sE 没有 toString。
const BattleTutorialAnonymousEffects = {
oE: 'movementHasStarted=true(`n.pq8(cd, cd, !0)`)',
sE: 'new rE() 整片复位(`new rE()`)',
};
// spectator 切片(HA / tR.jqm):XA/JA/ZA/YA 没有 toString。
const SpectatorAnonymousEffects = {
XA: 'spectators=整表(`n.yql(cd, cd, t.dqm_1)`)',
JA: 'spectators += `new iR(userId, uid, inSupportGroup, inPunishersGroup)`(`Hg`)',
ZA: 'spectators 按 userId 过滤掉一条(`Qd(s.kqm_1, t.iqm_1) || e.y(s)`)',
YA: 'spectatorsListVisible=布尔(`n.yql(cd, cd, cd, t.cqm_1)`)',
};
// antiAddiction 切片(UC / JC.iq8):FC/XC/YC 没有 toString。
const AntiAddictionAnonymousEffects = {
FC: 'isEnableRealNameCheck=true(`n.lq7(!0)`)',
XC: 'unknownUserId=true、urlForEnterUserId=参数(`n.lq7(cd, !0, t.hq8_1)`)',
YC: 'playingTimeInMinutes、alertMessage(`n.lq7(cd, cd, cd, t.fq8_1, t.gq8_1)`)',
};
// moderation 切片(NI / _A.oqh):13 个类全都没有 toString。
const ModerationAnonymousEffects = {
UI: 'moderatorMode / battleModeratorMode / eventOrganizerMode(`n.aqf(a, b, c)`)',
FI: '两个 URL 模板(`n.aqf(cd, cd, cd, t.vqg_1, t.wqg_1)`)',
XI: 'battleInfo + forceShowBattleInfo=true(`n.aqf(…, t.fqh_1, !0)`)',
JI: 'forceShowBattleInfo=false(`n.aqf(…, !1)`)',
GI: 'selectedUsers 去重后追加 `new OI(...)`(`Hg(e, new OI(...))`)',
WI: '同 GI,另一个派发点(`Hg(_, new OI(...))`)',
KI: 'selectedUsers 按 userId 过滤(`Qd(v.oqf_1, t.eqh_1) || l.y(v)`)',
YI: 'selectedUsers 清空(`var p = md()`)',
uA: 'selectedUsers 清空(`var w = md()`,另一个派发点)',
ZI: 'systemMessage + forceShowSystemMessage=true(`n.aqf(…, t.gqh_1, !0)`)',
QI: 'forceShowSystemMessage=false(`n.aqf(…, !1)`)',
tA: 'forceShowBlockUserMenu=true(`n.aqf(…, !0)`)',
nA: 'forceShowBlockUserMenu=false(`n.aqf(…, !1)`)',
};
// 迁移 / 注册共用(Sit.rtg 与 art.stg 都 instanceof 同一批类)。
const AuthAnonymousEffects = {
Mit: 'isDisabled=true(`n.its(!0)`,只有注册切片读)',
Dit: 'loginStatus / uidValidationStatus = FREE(`lrt()`)',
Lit: 'loginStatus / uidValidationStatus = INCORRECT(`frt()`)',
Bit: 'loginStatus / uidValidationStatus = UNKNOWN(`crt()`)',
Hit: 'loginStatus / uidValidationStatus = SHORT_NAME(`prt()`)',
Uit: 'registerStatus=PASSWORD_CORRECT(`(Iit(), Xv)`,只有注册切片读)',
Fit: 'registerStatus=PASSWORD_INCORRECT(`drt()`)',
Git: 'registerStatus=FAILED(`grt()`)',
hrt: 'registerStatus=UNKNOWN(`n.its(cd, cd, cd, cd, cd, wrt())`,只有注册切片读)',
_rt: 'login=""、两个状态复位(两个切片都读)',
};
fs.writeFileSync(
path.join(P, 'names', 'root_wiring.json'),
JSON.stringify(
@@ -1043,6 +1145,36 @@ fs.writeFileSync(
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(BattleTutorialAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(SpectatorAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(AntiAddictionAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(ModerationAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(AuthAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
],
Markers: [
{
@@ -0,0 +1,68 @@
// AntiAddictionReducer.kt —— 防沉迷切片(对应 module_10032 的 UC 状态 + JC.iq8)
//
// 证据链:
// 状态类 UC:8 个字段,toString 名 "AntiAddictionState(…)"。
// 默认:isEnableRealNameCheck=false、unknownUserId=false、urlForEnterUserId=null、
// playingTimeInMinutes=0、alertMessage=""、idNumber=""、realName=""、validateStatus=null。
// 根委派:`W.iq8(t, n.kpx_1)`。
// reducer:
// FC(无 toString) → isEnableRealNameCheck=true
// XC(无 toString) → unknownUserId=true、urlForEnterUserId=参数
// YC(无 toString) → playingTimeInMinutes、alertMessage
// HC(SetRealNameAndIdNumber) → idNumber/realName;参数为 null 时保留原值
// VC(SetValidateAntiAddictionUserDataStatus)→ validateStatus
package App.State.Reducers
import App.State.SetRealNameAndIdNumber
import App.State.SetValidateAntiAddictionUserDataStatus
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `FC`:开启实名认证检查(`n.lq7(!0)`)。 */
object EnableRealNameCheck
/** 原 `XC`:要求补录用户 id,并给出入口地址(`n.lq7(cd, !0, url)`)。 */
data class RequireUserIdVerification(val urlForEnterUserId: Any?)
/** 原 `YC`:更新累计在线时长与提示语。 */
data class UpdatePlayingTime(
val playingTimeInMinutes: Any?,
val alertMessage: String,
)
/* ------------------------------------------------------------------ 状态 */
data class AntiAddictionStateSlice(
val IsEnableRealNameCheck: Boolean = false,
val UnknownUserId: Boolean = false,
val UrlForEnterUserId: Any? = null,
val PlayingTimeInMinutes: Any? = 0,
val AlertMessage: String = "",
val IdNumber: Any? = "",
val RealName: Any? = "",
val ValidateStatus: Any? = null,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceAntiAddiction(
State0: AntiAddictionStateSlice,
Action: Any,
): AntiAddictionStateSlice = when (Action) {
is EnableRealNameCheck -> State0.copy(IsEnableRealNameCheck = true)
is RequireUserIdVerification -> State0.copy(
UnknownUserId = true,
UrlForEnterUserId = Action.urlForEnterUserId,
)
is UpdatePlayingTime -> State0.copy(
PlayingTimeInMinutes = Action.playingTimeInMinutes,
AlertMessage = Action.alertMessage,
)
is SetRealNameAndIdNumber -> State0.copy(
IdNumber = Action.idNumber ?: State0.IdNumber,
RealName = Action.realName ?: State0.RealName,
)
is SetValidateAntiAddictionUserDataStatus ->
State0.copy(ValidateStatus = Action.validateStatus)
else -> State0
}
@@ -0,0 +1,36 @@
// AuthActions.kt —— 登录 / 迁移 / 注册共用的匿名动作
//
// 证据链:module_10032 里这些动作类只定义一次(16907–16965),
// 迁移切片(Sit.rtg)和注册切片(art.stg)都直接 `instanceof` 它们,
// 因此 Kotlin 侧也只能有一份,两个 reducer 共同响应、各自动自己读的字段。
package App.State.Reducers
/** 原 `Mit`:注册按钮置灰(只有注册切片读)。 */
object DisableRegister
/** 原 `Dit`:uid 可用(迁移的 loginStatus / 注册的 uidValidationStatus)。 */
object UidIsFree
/** 原 `Lit`:uid 不合法。 */
object UidIsIncorrect
/** 原 `Bit`:uid 校验状态复位为 UNKNOWN。 */
object ResetUidStatus
/** 原 `Hit`:uid 过短。 */
object UidTooShort
/** 原 `Uit`:注册密码校验通过(只有注册切片读)。 */
object RegisterPasswordCorrect
/** 原 `Fit`:注册密码错误。 */
object RegisterPasswordIncorrect
/** 原 `Git`:注册失败。 */
object RegisterFailed
/** 原 `hrt`:注册状态复位为 UNKNOWN(只有注册切片读)。 */
object ResetRegisterStatus
/** 原 `_rt`:清空 login 并把两个状态复位(迁移与注册都读)。 */
object ResetAuthenticationForm
@@ -0,0 +1,44 @@
// BattleTutorialReducer.kt —— 战斗内上手指引切片(对应 module_10032 的 rE 状态 + hE.sq8)
//
// 证据链:
// 状态类 rE:3 个字段,toString 名 "BattleTutorialState(…)"。
// 默认:message=null、fragsCount=0、movementHasStarted=false。
// 根委派:`Q.sq8(t, n.cpv_1)`。
// reducer:
// eE(ShowMessage) → message
// _E(SetFragsCount) → fragsCount
// oE(无 toString) → movementHasStarted=true
// sE(无 toString) → `new rE()` 整片复位
package App.State.Reducers
import App.State.SetFragsCount
import App.State.ShowMessage
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `oE`:玩家开始移动,指引进入下一步。 */
object MarkMovementStarted
/** 原 `sE`:复位整片指引状态(`new rE()`)。 */
object ResetBattleTutorial
/* ------------------------------------------------------------------ 状态 */
data class BattleTutorialStateSlice(
val Message: Any? = null,
val FragsCount: Any? = 0,
val MovementHasStarted: Boolean = false,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceBattleTutorial(
State0: BattleTutorialStateSlice,
Action: Any,
): BattleTutorialStateSlice = when (Action) {
is ShowMessage -> State0.copy(Message = Action.message)
is SetFragsCount -> State0.copy(FragsCount = Action.fragsCount)
is MarkMovementStarted -> State0.copy(MovementHasStarted = true)
is ResetBattleTutorial -> BattleTutorialStateSlice()
else -> State0
}
@@ -0,0 +1,47 @@
// ContextTutorialReducer.kt —— 上手指引切片(对应 module_10032 的 Mst 状态 + Xst.tth)
//
// 证据链:
// 状态类 Mst:4 个字段,toString 名 "ContextTutorialState(…)"。
// 默认:currentElement=null、loadedElements=emptyMap()、unViewedElements=emptyList()、missionToElement=emptyMap()。
// 根委派:`Jst().tth(t, n.ypx_1)`。
// reducer:
// Lst(LoadCurrentElement) → currentElement
// Bst(SetUnViewedElements) → unViewedElements
// Fst(MarkElementAsViewed) → unViewedElements 去掉 element(`Ym` = List.minus)
// Ust(SetLoadedElements) → loadedElements
// Gst(SetMissionToElement) → missionToElement
package App.State.Reducers
import App.State.LoadCurrentElement
import App.State.MarkElementAsViewed
import App.State.SetLoadedElements
import App.State.SetMissionToElement
import App.State.SetUnViewedElements
/* ------------------------------------------------------------------ 状态 */
data class ContextTutorialStateSlice(
val CurrentElement: Any? = null,
val LoadedElements: Map<Any?, Any?> = emptyMap(),
val UnViewedElements: List<Any?> = emptyList(),
val MissionToElement: Map<String, String> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
@Suppress("UNCHECKED_CAST")
private fun AsMap(Value: Any?): Map<Any?, Any?> =
Value as? Map<Any?, Any?> ?: emptyMap()
fun ReduceContextTutorial(
State0: ContextTutorialStateSlice,
Action: Any,
): ContextTutorialStateSlice = when (Action) {
is LoadCurrentElement -> State0.copy(CurrentElement = Action.element)
is SetUnViewedElements -> State0.copy(UnViewedElements = Action.unViewedElements)
is MarkElementAsViewed ->
State0.copy(UnViewedElements = State0.UnViewedElements - Action.element)
is SetLoadedElements -> State0.copy(LoadedElements = AsMap(Action.loadedElements))
is SetMissionToElement -> State0.copy(MissionToElement = Action.missionToElement)
else -> State0
}
@@ -0,0 +1,102 @@
// MigrationStateReducer.kt —— 老账号迁移切片(对应 module_10032 的 $it 状态 + Sit.rtg)
//
// 证据链:
// 状态类 $it:7 个字段,toString 名 "MigrationState(…)";copy 是 jtr(7 字段 + 回调)。
// 默认:requestLogin=false、login=""、forceLogin=null、password=""、
// loginStatus=UNKNOWN、registerStatus=UNKNOWN、advisedUids=emptyList()。
// 根委派:`Uv.rtg(t, n.ipv_1)`。
// reducer(11 个实例臂):
// Kit(SetUid) → login + 两个状态复位
// Jit(ForceSetUid) → forceLogin + 两个状态复位
// Yit(SetPassword) → password + registerStatus 复位
// Dit(UidIsFree) → loginStatus=FREE
// Oit(EnteredUidIsBusy)→ loginStatus=BUSY + advisedUids
// Lit(UidIsIncorrect) → loginStatus=INCORRECT
// Bit(ResetUidStatus) → loginStatus=UNKNOWN
// Fit(RegisterPasswordIncorrect)→ registerStatus=PASSWORD_INCORRECT
// Git(RegisterFailed) → registerStatus=FAILED
// Hit(UidTooShort) → loginStatus=SHORT_NAME
// _rt(ResetAuthenticationForm)→ login="" + 两个状态复位
//
// 状态枚举直接对应源码 `Eit()` / `Iit()`:
// LoginStatus = UNKNOWN / FREE / INCORRECT / BUSY / SHORT_NAME
// RegisterStatus = UNKNOWN / PASSWORD_CORRECT / PASSWORD_INCORRECT / FAILED
//
// 注意:`SetUid` / `ForceSetUid` / `SetPassword` 与注册、登录切片共用同一批动作类
// (原包也是同一批类),因此几片 reducer 会同时响应它们,各自动自己读的字段。
package App.State.Reducers
import App.State.EnteredUidIsBusy
import App.State.ForceSetUid
import App.State.SetPassword
import App.State.SetUid
/* ------------------------------------------------------------------ 枚举 */
enum class MigrationLoginStatus {
UNKNOWN,
FREE,
INCORRECT,
BUSY,
SHORT_NAME,
}
enum class MigrationRegisterStatus {
UNKNOWN,
PASSWORD_CORRECT,
PASSWORD_INCORRECT,
FAILED,
}
/* ------------------------------------------------------------------ 状态 */
data class MigrationStateSlice(
val RequestLogin: Boolean = false,
val Login: String = "",
val ForceLogin: Any? = null,
val Password: String = "",
val LoginStatus: MigrationLoginStatus = MigrationLoginStatus.UNKNOWN,
val RegisterStatus: MigrationRegisterStatus = MigrationRegisterStatus.UNKNOWN,
val AdvisedUids: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceMigrationState(
State0: MigrationStateSlice,
Action: Any,
): MigrationStateSlice = when (Action) {
// Kit / Jit / Yit 都会把两个状态打回 UNKNOWN(原 `crt()` / `wrt()`)。
is SetUid -> State0.copy(
Login = Action.uid?.toString() ?: "",
LoginStatus = MigrationLoginStatus.UNKNOWN,
RegisterStatus = MigrationRegisterStatus.UNKNOWN,
)
is ForceSetUid -> State0.copy(
ForceLogin = Action.uid,
LoginStatus = MigrationLoginStatus.UNKNOWN,
RegisterStatus = MigrationRegisterStatus.UNKNOWN,
)
is SetPassword -> State0.copy(
Password = Action.password?.toString() ?: "",
RegisterStatus = MigrationRegisterStatus.UNKNOWN,
)
is UidIsFree -> State0.copy(LoginStatus = MigrationLoginStatus.FREE)
is EnteredUidIsBusy -> State0.copy(
LoginStatus = MigrationLoginStatus.BUSY,
AdvisedUids = Action.advisedUids as? List<Any?> ?: emptyList(),
)
is UidIsIncorrect -> State0.copy(LoginStatus = MigrationLoginStatus.INCORRECT)
is ResetUidStatus -> State0.copy(LoginStatus = MigrationLoginStatus.UNKNOWN)
is UidTooShort -> State0.copy(LoginStatus = MigrationLoginStatus.SHORT_NAME)
is RegisterPasswordIncorrect ->
State0.copy(RegisterStatus = MigrationRegisterStatus.PASSWORD_INCORRECT)
is RegisterFailed ->
State0.copy(RegisterStatus = MigrationRegisterStatus.FAILED)
is ResetAuthenticationForm -> State0.copy(
Login = "",
LoginStatus = MigrationLoginStatus.UNKNOWN,
RegisterStatus = MigrationRegisterStatus.UNKNOWN,
)
else -> State0
}
@@ -0,0 +1,158 @@
// ModerationReducer.kt —— 管理员工具切片(对应 module_10032 的 NI 状态 + _A.oqh)
//
// 证据链:
// 状态类 NI:11 个字段,toString 名 "Moderation(…)"。
// 默认:三个模式 false、两个 URL 模板 ""、battleInfo=new MI()、三个 forceShow false、
// systemMessage=""、selectedUsers=emptyList()。
// 根委派:`Ei.oqh(t, n.mpx_1)`。
// reducer(13 个实例臂):
// UI(无 toString) → 三个模式开关
// FI(无 toString) → 两个 URL 模板
// XI(无 toString) → battleInfo + forceShowBattleInfo=true
// JI(无 toString) → forceShowBattleInfo=false
// GI(无 toString) → selectedUsers 先按 userId 去重、再追加 `new OI(...)`
// WI(无 toString) → 同 GI(另一个派发点,效果一致)
// KI(无 toString) → selectedUsers 按 userId 移除
// YI(无 toString) → selectedUsers 清空
// uA(无 toString) → selectedUsers 清空(同 YI,另一个派发点)
// ZI(无 toString) → systemMessage + forceShowSystemMessage=true
// QI(无 toString) → forceShowSystemMessage=false
// tA(无 toString) → forceShowBlockUserMenu=true
// nA(无 toString) → forceShowBlockUserMenu=false
//
// 说明:`battleInfo` 的原模型 `MI` 有 13 个字段(战斗信息面板),属战斗层,这里按
// `Any?` 收口(缺省 null),只在 ShowBattleInfo 时整体替换。
package App.State.Reducers
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `UI`:一次写三个管理员模式开关。 */
data class SetModeratorModes(
val moderatorMode: Boolean,
val battleModeratorMode: Boolean,
val eventOrganizerMode: Boolean,
)
/** 原 `FI`:写管理员工具的战斗 / 用户链接模板。 */
data class SetModerationUrlTemplates(
val battleUrlTemplate: String,
val userUrlTemplate: String,
)
/** 原 `XI`:显示战斗信息面板。 */
data class ShowBattleInfo(val battleInfo: Any?)
/** 原 `JI`:收起战斗信息面板。 */
object HideBattleInfo
/** 原 `GI`:把用户加入选中列表(先按 userId 去重)。 */
data class AddSelectedUser(
val userId: Any?,
val userRank: Any?,
val userName: Any?,
)
/** 原 `WI`:另一个派发点上把用户加入选中列表,效果与 `GI` 相同。 */
data class AddSelectedUserFromChat(
val userId: Any?,
val userRank: Any?,
val userName: Any?,
)
/** 原 `KI`:从选中列表移除用户。 */
data class RemoveSelectedUser(val userId: Any?)
/** 原 `YI`:清空选中列表。 */
object ClearSelectedUsers
/** 原 `uA`:另一个派发点上清空选中列表,效果与 `YI` 相同。 */
object ResetSelectedUsers
/** 原 `ZI`:显示管理员系统消息。 */
data class ShowModerationSystemMessage(val message: String)
/** 原 `QI`:收起管理员系统消息。 */
object HideModerationSystemMessage
/** 原 `tA`:强制弹出封禁用户菜单。 */
object ShowBlockUserMenu
/** 原 `nA`:收起封禁用户菜单。 */
object HideBlockUserMenu
/* ------------------------------------------------------------------ 模型 */
data class ModerationUserRecord(
val userId: Any?,
val userRank: Any?,
val userName: Any?,
)
/* ------------------------------------------------------------------ 状态 */
data class ModerationStateSlice(
val ModeratorMode: Boolean = false,
val BattleModeratorMode: Boolean = false,
val EventOrganizerMode: Boolean = false,
val ModerationToolBattleUrlTemplate: String = "",
val ModerationToolUserUrlTemplate: String = "",
val BattleInfo: Any? = null,
val ForceShowBattleInfo: Boolean = false,
val SystemMessage: String = "",
val ForceShowSystemMessage: Boolean = false,
val ForceShowBlockUserMenu: Boolean = false,
val SelectedUsers: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
/** `Hg(n.yqe_1, new OI(...))` 的等价:先去掉同 userId 的旧记录,再追加。 */
private fun UpsertUser(Users: List<Any?>, Record: ModerationUserRecord): List<Any?> =
Users.filterNot { (it as? ModerationUserRecord)?.userId == Record.userId } + Record
fun ReduceModeration(
State0: ModerationStateSlice,
Action: Any,
): ModerationStateSlice = when (Action) {
is SetModeratorModes -> State0.copy(
ModeratorMode = Action.moderatorMode,
BattleModeratorMode = Action.battleModeratorMode,
EventOrganizerMode = Action.eventOrganizerMode,
)
is SetModerationUrlTemplates -> State0.copy(
ModerationToolBattleUrlTemplate = Action.battleUrlTemplate,
ModerationToolUserUrlTemplate = Action.userUrlTemplate,
)
is ShowBattleInfo -> State0.copy(
BattleInfo = Action.battleInfo,
ForceShowBattleInfo = true,
)
is HideBattleInfo -> State0.copy(ForceShowBattleInfo = false)
is AddSelectedUser -> State0.copy(
SelectedUsers = UpsertUser(
State0.SelectedUsers,
ModerationUserRecord(Action.userId, Action.userRank, Action.userName),
),
)
is AddSelectedUserFromChat -> State0.copy(
SelectedUsers = UpsertUser(
State0.SelectedUsers,
ModerationUserRecord(Action.userId, Action.userRank, Action.userName),
),
)
is RemoveSelectedUser -> State0.copy(
SelectedUsers = State0.SelectedUsers.filterNot {
(it as? ModerationUserRecord)?.userId == Action.userId
},
)
is ClearSelectedUsers -> State0.copy(SelectedUsers = emptyList())
is ResetSelectedUsers -> State0.copy(SelectedUsers = emptyList())
is ShowModerationSystemMessage -> State0.copy(
SystemMessage = Action.message,
ForceShowSystemMessage = true,
)
is HideModerationSystemMessage -> State0.copy(ForceShowSystemMessage = false)
is ShowBlockUserMenu -> State0.copy(ForceShowBlockUserMenu = true)
is HideBlockUserMenu -> State0.copy(ForceShowBlockUserMenu = false)
else -> State0
}
@@ -0,0 +1,128 @@
// RegisterReducer.kt —— 注册切片(对应 module_10032 的 Cit 状态 + art.stg)
//
// 证据链:
// 状态类 Cit:16 个字段,toString 名 "RegisterState(…)";copy 是 its(16 字段 + 回调)。
// 默认:isDisabled=false、login=""、forceLogin=null、password=""、
// uidValidationStatus=UNKNOWN、registerStatus=UNKNOWN、advisedUids=emptyList()、
// socialNetworkAvailability=emptyMap()、socialNetworkClientIds=emptyMap()、
// socialNetworkOAuthData=null、minPasswordLength=6、maxPasswordLength=20、
// 三个 passwordNeed* 为 false、openedPassword=true。
// 根委派:`cp.stg(t, n.hpv_1)`。
// reducer(19 个实例臂):
// Mit(DisableRegister)→ isDisabled=true
// Kit(SetUid) → login;uidValidationStatus=UNKNOWN;若原状态不是 INCORRECT,
// registerStatus 也复位(原 `2 === n.wtr_1.k3_1` 分支)
// Jit(ForceSetUid)→ forceLogin;状态复位规则同上
// Yit(SetPassword)→ password + registerStatus=UNKNOWN
// Dit / Oit / Lit / Bit / Hit → uidValidationStatus = FREE / BUSY(+advisedUids) / INCORRECT / UNKNOWN / SHORT_NAME
// Uit / Fit / Git → registerStatus = PASSWORD_CORRECT / PASSWORD_INCORRECT / FAILED
// trt(SocialNetworkAvailability)/ nrt(SocialNetworkClientIds)/ ert(ReceiveOAuthUri)
// _rt(ResetAuthenticationForm)→ login="" + 两个状态复位
// hrt(ResetRegisterStatus)→ registerStatus=UNKNOWN
// Nit(SetPasswordRestrictions)→ 5 个密码规则字段
// ort(SetOpenedPassword)→ openedPassword
package App.State.Reducers
import App.State.EnteredUidIsBusy
import App.State.ForceSetUid
import App.State.ReceiveOAuthUri
import App.State.SetOpenedPassword
import App.State.SetPassword
import App.State.SetPasswordRestrictions
import App.State.SetUid
import App.State.SocialNetworkAvailability
import App.State.SocialNetworkClientIds
/* ------------------------------------------------------------------ 枚举 */
enum class UidStatus {
UNKNOWN,
FREE,
INCORRECT,
BUSY,
SHORT_NAME,
}
enum class RegistrationStatus {
UNKNOWN,
PASSWORD_CORRECT,
PASSWORD_INCORRECT,
FAILED,
}
/* ------------------------------------------------------------------ 状态 */
data class RegisterStateSlice(
val IsDisabled: Boolean = false,
val Login: String = "",
val ForceLogin: Any? = null,
val Password: String = "",
val UidValidationStatus: UidStatus = UidStatus.UNKNOWN,
val RegisterStatus: RegistrationStatus = RegistrationStatus.UNKNOWN,
val AdvisedUids: List<Any?> = emptyList(),
val SocialNetworkAvailability: Map<String, Boolean> = emptyMap(),
val SocialNetworkClientIds: Any? = emptyMap<String, Any?>(),
val SocialNetworkOAuthData: Any? = null,
val MinPasswordLength: Int = 6,
val MaxPasswordLength: Int = 20,
val PasswordNeedLower: Boolean = false,
val PasswordNeedUpper: Boolean = false,
val PasswordNeedDigit: Boolean = false,
val OpenedPassword: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
/** `Kit` / `Jit` 共用:uid 变化时复位校验状态;原本不是 INCORRECT 才连注册状态一起复位。 */
private fun ResetUidRelated(State0: RegisterStateSlice): RegisterStateSlice = State0.copy(
UidValidationStatus = UidStatus.UNKNOWN,
RegisterStatus =
if (State0.UidValidationStatus == UidStatus.INCORRECT) State0.RegisterStatus
else RegistrationStatus.UNKNOWN,
)
fun ReduceRegister(
State0: RegisterStateSlice,
Action: Any,
): RegisterStateSlice = when (Action) {
is DisableRegister -> State0.copy(IsDisabled = true)
is SetUid -> ResetUidRelated(State0.copy(Login = Action.uid?.toString() ?: ""))
is ForceSetUid -> ResetUidRelated(State0.copy(ForceLogin = Action.uid))
is SetPassword -> State0.copy(
Password = Action.password?.toString() ?: "",
RegisterStatus = RegistrationStatus.UNKNOWN,
)
is UidIsFree -> State0.copy(UidValidationStatus = UidStatus.FREE)
is EnteredUidIsBusy -> State0.copy(
UidValidationStatus = UidStatus.BUSY,
AdvisedUids = Action.advisedUids as? List<Any?> ?: emptyList(),
)
is UidIsIncorrect -> State0.copy(UidValidationStatus = UidStatus.INCORRECT)
is ResetUidStatus -> State0.copy(UidValidationStatus = UidStatus.UNKNOWN)
is UidTooShort -> State0.copy(UidValidationStatus = UidStatus.SHORT_NAME)
is RegisterPasswordCorrect ->
State0.copy(RegisterStatus = RegistrationStatus.PASSWORD_CORRECT)
is RegisterPasswordIncorrect ->
State0.copy(RegisterStatus = RegistrationStatus.PASSWORD_INCORRECT)
is RegisterFailed -> State0.copy(RegisterStatus = RegistrationStatus.FAILED)
is ResetRegisterStatus -> State0.copy(RegisterStatus = RegistrationStatus.UNKNOWN)
is SocialNetworkAvailability ->
State0.copy(SocialNetworkAvailability = Action.socialNetworkAvailability)
is SocialNetworkClientIds ->
State0.copy(SocialNetworkClientIds = Action.socialNetworkClientIds)
is ReceiveOAuthUri -> State0.copy(SocialNetworkOAuthData = Action.startOAuthData)
is ResetAuthenticationForm -> State0.copy(
Login = "",
UidValidationStatus = UidStatus.UNKNOWN,
RegisterStatus = RegistrationStatus.UNKNOWN,
)
is SetPasswordRestrictions -> State0.copy(
MinPasswordLength = Action.minPasswordLength,
MaxPasswordLength = Action.maxPasswordLength,
PasswordNeedLower = Action.passwordNeedLower,
PasswordNeedUpper = Action.passwordNeedUpper,
PasswordNeedDigit = Action.passwordNeedDigit,
)
is SetOpenedPassword -> State0.copy(OpenedPassword = Action.openedPassword)
else -> State0
}
@@ -0,0 +1,75 @@
// SpectatorReducer.kt —— 观战切片(对应 module_10032 的 HA 状态 + tR.jqm)
//
// 证据链:
// 状态类 HA:4 个字段,toString 名 "Spectator(…)"。
// 默认:isSpectator=false、spectatorHudLayout=new nR()、spectators=emptyList()、spectatorsListVisible=true。
// 根委派:`Li.jqm(t, n.lpx_1)`。
// reducer:
// WA(SetSpectator) → isSpectator
// VA(UpdateHudLayout) → spectatorHudLayout
// XA(无 toString) → spectators 整表替换
// JA(无 toString) → spectators 追加 `new iR(...)`(SpectatorInfo 四字段)
// ZA(无 toString) → spectators 按 userId 过滤掉一个
// YA(无 toString) → spectatorsListVisible
//
// 说明:`spectatorHudLayout` 的默认值是 `new nR()`(15 字段的 HUD 布局对象),
// 该模型属于战斗层,这里按 `Any?` 收口(缺省 null),只在收到 UpdateHudLayout 时替换。
package App.State.Reducers
import App.State.SetSpectator
import App.State.SpectatorInfo
import App.State.UpdateHudLayout
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `XA`:整体替换观战者列表。 */
data class SetSpectators(val spectators: List<Any?>)
/** 原 `JA`:追加一个观战者(`new iR(userId, uid, inSupportGroup, inPunishersGroup)`)。 */
data class AddSpectator(
val userId: Long,
val uid: Any?,
val inSupportGroup: Boolean,
val inPunishersGroup: Boolean,
)
/** 原 `ZA`:按 userId 移除观战者。 */
data class RemoveSpectator(val userId: Long)
/** 原 `YA`:切换观战者列表可见性。 */
data class SetSpectatorsListVisible(val visible: Boolean)
/* ------------------------------------------------------------------ 状态 */
data class SpectatorStateSlice(
val IsSpectator: Boolean = false,
val SpectatorHudLayout: Any? = null,
val Spectators: List<Any?> = emptyList(),
val SpectatorsListVisible: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceSpectator(
State0: SpectatorStateSlice,
Action: Any,
): SpectatorStateSlice = when (Action) {
is SetSpectator -> State0.copy(IsSpectator = Action.spectatorMode)
is UpdateHudLayout -> State0.copy(SpectatorHudLayout = Action.hudLayout)
is SetSpectators -> State0.copy(Spectators = Action.spectators)
is AddSpectator -> State0.copy(
Spectators = State0.Spectators + SpectatorInfo(
userId = Action.userId,
uid = Action.uid,
inSupportGroup = Action.inSupportGroup,
inPunishersGroup = Action.inPunishersGroup,
),
)
is RemoveSpectator -> State0.copy(
Spectators = State0.Spectators.filterNot {
(it as? SpectatorInfo)?.userId == Action.userId
},
)
is SetSpectatorsListVisible -> State0.copy(SpectatorsListVisible = Action.visible)
else -> State0
}
@@ -0,0 +1,32 @@
// SystemReducer.kt —— 全屏系统消息切片(对应 module_10032 的 _9 状态 + p9.xt9)
//
// 证据链:
// 状态类 _9:3 个字段,toString 名 "SystemScreenState(message=…, restart=…, fatalError=…)"。
// 默认:message=null、restart=false、fatalError=null。
// 根委派:`w9().xt9(t, n.mpv_1)`。
// reducer:
// v9(DoShowMessage) → message、restart
// SetFatalError → fatalError
package App.State.Reducers
import App.State.DoShowMessage
import App.State.SetFatalError
/* ------------------------------------------------------------------ 状态 */
data class SystemStateSlice(
val Message: Any? = null,
val Restart: Boolean = false,
val FatalError: Any? = null,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceSystem(
State0: SystemStateSlice,
Action: Any,
): SystemStateSlice = when (Action) {
is DoShowMessage -> State0.copy(Message = Action.message, Restart = Action.restart)
is SetFatalError -> State0.copy(FatalError = Action.fatalError)
else -> State0
}
@@ -0,0 +1,55 @@
// TwoStepVerificationReducer.kt —— 两步验证切片(对应 module_10032 的 x9 状态 + P9.atb)
//
// 证据链:
// 状态类 x9:6 个字段,toString 名 "TwoStepVerificationState(…)"。
// 默认:isUse2FA=false、isActivated2FA=false、qrCodeDataURI=null、sharedSecret=null、
// verificationStatus=null、enable2FaRequest=false。
// 根委派:`_f.atb(t, n.tpx_1)`。
// reducer:
// j9(SetUse2FA) → isUse2FA
// S9(ActivationStatus2FA) → isActivated2FA
// C9(SetSecretAndQRCode) → qrCodeDataURI、sharedSecret
// I9(VerificationStatus) → null → verificationStatus=null
// false → verificationStatus=false
// true → isActivated2FA=true(成功即视为已启用)
// A9(Enable2FaRequest) → enable2FaRequest
package App.State.Reducers
import App.State.ActivationStatus2FA
import App.State.Enable2FaRequest
import App.State.SetSecretAndQRCode
import App.State.SetUse2FA
import App.State.VerificationStatus
/* ------------------------------------------------------------------ 状态 */
data class TwoStepVerificationStateSlice(
val IsUse2FA: Boolean = false,
val IsActivated2FA: Boolean = false,
val QrCodeDataURI: Any? = null,
val SharedSecret: Any? = null,
val VerificationStatus: Any? = null,
val Enable2FaRequest: Boolean = false,
)
/* ------------------------------------------------------------------ reducer */
fun ReduceTwoStepVerification(
State0: TwoStepVerificationStateSlice,
Action: Any,
): TwoStepVerificationStateSlice = when (Action) {
is SetUse2FA -> State0.copy(IsUse2FA = Action.isUse2FA)
is ActivationStatus2FA -> State0.copy(IsActivated2FA = Action.isActivated)
is SetSecretAndQRCode -> State0.copy(
QrCodeDataURI = Action.qrCodeDataURI,
SharedSecret = Action.sharedSecret,
)
is VerificationStatus -> when (val Success = Action.isValidationSuccess) {
null -> State0.copy(VerificationStatus = null)
false -> State0.copy(VerificationStatus = false)
true -> State0.copy(IsActivated2FA = true)
else -> State0
}
is Enable2FaRequest -> State0.copy(Enable2FaRequest = Action.enable)
else -> State0
}
@@ -0,0 +1,89 @@
// UserReportsReducer.kt —— 举报切片(对应 module_10032 的 Zst 状态 + qot.zth)
//
// 证据链:
// 状态类 Zst:5 个字段,toString 名 "UserReportsState(…)"。
// 默认:isEnabled=false、helpdeskTicketsEnabled=false、availableReports=0、
// availableGameReports=0、reports=(原 `dsv()` 从 localStorage 的 SENT_REPORTS 读回)。
// 根委派:`(null == sd && new qot(), sd).zth(t, n.dpy_1)`。
// reducer(6 个实例臂):
// cot(InitUserReports) → 4 个开关/额度字段
// lot(SetAvailableReports) → availableReports
// fot(SetAvailableGameReports)→ availableGameReports
// vot(ReportProBattleName) → reports 追加 BATTLE_NAME 记录(有效期 +1 天)
// pot(ReportLobbyChatMessage) → reports 追加 LOBBY_CHAT_MESSAGE 记录
// wot(ReportBattleChatMessage)→ reports 追加 BATTLE_CHAT_MESSAGE 记录
//
// 原 reducer 在 reports 变化后会把列表序列化写回 localStorage 的 SENT_REPORTS
// (以及 Init 时从里面过滤过期项读回)。这属于 storage port 的职责,纯 reducer 不做 IO:
// Init 保留当前列表,写回留给中间件层。
package App.State.Reducers
import App.State.InitUserReports
import App.State.ReportBattleChatMessage
import App.State.ReportLobbyChatMessage
import App.State.ReportProBattleName
import App.State.SetAvailableGameReports
import App.State.SetAvailableReports
/* ------------------------------------------------------------------ 模型 */
/** 原 `dot` 枚举:BATTLE_NAME / LOBBY_CHAT_MESSAGE / BATTLE_CHAT_MESSAGE。 */
enum class ReportKind {
BATTLE_NAME,
LOBBY_CHAT_MESSAGE,
BATTLE_CHAT_MESSAGE,
}
/** 原 `mot(type, targetId, expireTime)`:一条已发送的举报。 */
data class ReportEntry(
val kind: ReportKind,
val targetId: Any?,
val expireTime: Double,
)
/* ------------------------------------------------------------------ 时间 */
/** 原 `Fg(Md(), new Dd(864e5, 0))`:当前时间 + 1 天。 */
private fun ExpireAtNextDay(): Double = (js("Date.now() + 86400000") as Number).toDouble()
/* ------------------------------------------------------------------ 状态 */
data class UserReportsStateSlice(
val IsEnabled: Boolean = false,
val HelpdeskTicketsEnabled: Boolean = false,
val AvailableReports: Any? = 0,
val AvailableGameReports: Any? = 0,
val Reports: List<Any?> = emptyList(),
)
/* ------------------------------------------------------------------ reducer */
private fun AppendReport(State0: UserReportsStateSlice, Entry: ReportEntry): UserReportsStateSlice =
State0.copy(Reports = State0.Reports + Entry)
fun ReduceUserReports(
State0: UserReportsStateSlice,
Action: Any,
): UserReportsStateSlice = when (Action) {
is InitUserReports -> State0.copy(
IsEnabled = Action.isEnabled,
HelpdeskTicketsEnabled = Action.helpdeskTicketsEnabled,
AvailableReports = Action.availableReports,
AvailableGameReports = Action.availableGameReports,
)
is SetAvailableReports -> State0.copy(AvailableReports = Action.availableReports)
is SetAvailableGameReports -> State0.copy(AvailableGameReports = Action.availableGameReports)
is ReportProBattleName -> AppendReport(
State0,
ReportEntry(ReportKind.BATTLE_NAME, Action.battleId, ExpireAtNextDay()),
)
is ReportLobbyChatMessage -> AppendReport(
State0,
ReportEntry(ReportKind.LOBBY_CHAT_MESSAGE, Action.messageId, ExpireAtNextDay()),
)
is ReportBattleChatMessage -> AppendReport(
State0,
ReportEntry(ReportKind.BATTLE_CHAT_MESSAGE, Action.messageId, ExpireAtNextDay()),
)
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 43 个切片里,42 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 53 个切片里,52 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -45,17 +45,30 @@
// creatorTagState → ReduceCreatorTag(qO / ze.er2,根委派 `ze.er2(t, n.spx_1)`)
// rewardedVideosState → ReduceRewardedVideos(JQ / xh.zsl,根委派 `xh.zsl(t, n.upx_1)`)
// passwordChangeState → ReducePasswordChange(VD / uu.pr6,根委派 `uu.pr6(t, n.lpv_1)`)
// system → ReduceSystem(_9 / w9().xt9,根委派 `w9().xt9(t, n.mpv_1)`)
// contextTutorial → ReduceContextTutorial(Mst / Jst().tth,根委派 `Jst().tth(t, n.ypx_1)`)
// battleTutorial → ReduceBattleTutorial(rE / Q.sq8,根委派 `Q.sq8(t, n.cpv_1)`)
// spectator → ReduceSpectator(HA / Li.jqm,根委派 `Li.jqm(t, n.lpx_1)`)
// twoStepVerificationState → ReduceTwoStepVerification(x9 / _f.atb,根委派 `_f.atb(t, n.tpx_1)`)
// antiAddiction → ReduceAntiAddiction(UC / W.iq8,根委派 `W.iq8(t, n.kpx_1)`)
// moderation → ReduceModeration(NI / Ei.oqh,根委派 `Ei.oqh(t, n.mpx_1)`)
// migrationState → ReduceMigrationState($it / Uv.rtg,根委派 `Uv.rtg(t, n.ipv_1)`)
// register → ReduceRegister(Cit / cp.stg,根委派 `cp.stg(t, n.hpv_1)`)
// userReportsState → ReduceUserReports(Zst / qot.zth,根委派 `(null == sd && new qot(), sd).zth(t, n.dpy_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 46 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 36 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
import App.State.Reducers.ReduceAccountSettings
import App.State.Reducers.ReduceAnnouncement
import App.State.Reducers.ReduceAntiAddiction
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ReduceBattleTutorial
import App.State.Reducers.ReduceCaptcha
import App.State.Reducers.ReduceContextTutorial
import App.State.Reducers.ReduceCreatorTag
import App.State.Reducers.ReduceDroppableGold
import App.State.Reducers.ReduceEntrance
@@ -72,7 +85,9 @@ import App.State.Reducers.ReduceLootBoxes
import App.State.Reducers.ReduceMainQuest
import App.State.Reducers.ReduceAvailabilityByRank
import App.State.Reducers.ReduceLobbyLoader
import App.State.Reducers.ReduceMigrationState
import App.State.Reducers.ReduceModalScreen
import App.State.Reducers.ReduceModeration
import App.State.Reducers.ReduceNews
import App.State.Reducers.ReduceNotificationByCountry
import App.State.Reducers.ReduceNotifications
@@ -84,16 +99,21 @@ import App.State.Reducers.ReduceQuestCategories
import App.State.Reducers.ReduceQuests
import App.State.Reducers.ReduceRankUpDialog
import App.State.Reducers.ReduceReferralEvent
import App.State.Reducers.ReduceRegister
import App.State.Reducers.ReduceRewardedVideos
import App.State.Reducers.ReduceRulesUpdated
import App.State.Reducers.ReduceScreen
import App.State.Reducers.ReduceSecuritySettings
import App.State.Reducers.ReduceSpectator
import App.State.Reducers.ReduceSupportUrl
import App.State.Reducers.ReduceSettings
import App.State.Reducers.ReduceShop
import App.State.Reducers.ReduceSystem
import App.State.Reducers.ReduceTips
import App.State.Reducers.ReduceTwitch
import App.State.Reducers.ReduceTwoStepVerification
import App.State.Reducers.ReduceUser
import App.State.Reducers.ReduceUserReports
/** 根 reducer:`k6`。 */
object RootReducer {
@@ -150,6 +170,16 @@ object RootReducer {
CreatorTagState = ReduceCreatorTag(State0.CreatorTagState, Action),
RewardedVideosState = ReduceRewardedVideos(State0.RewardedVideosState, Action),
PasswordChangeState = ReducePasswordChange(State0.PasswordChangeState, Action),
System = ReduceSystem(State0.System, Action),
ContextTutorial = ReduceContextTutorial(State0.ContextTutorial, Action),
BattleTutorial = ReduceBattleTutorial(State0.BattleTutorial, Action),
Spectator = ReduceSpectator(State0.Spectator, Action),
TwoStepVerificationState = ReduceTwoStepVerification(State0.TwoStepVerificationState, Action),
AntiAddiction = ReduceAntiAddiction(State0.AntiAddiction, Action),
Moderation = ReduceModeration(State0.Moderation, Action),
MigrationState = ReduceMigrationState(State0.MigrationState, Action),
Register = ReduceRegister(State0.Register, Action),
UserReportsState = ReduceUserReports(State0.UserReportsState, Action),
)
}
@@ -6,9 +6,12 @@ package App.State
import App.State.Reducers.AccountSettingsStateSlice
import App.State.Reducers.AnnouncementStateSlice
import App.State.Reducers.AntiAddictionStateSlice
import App.State.Reducers.AvailabilityByRankStateSlice
import App.State.Reducers.BattleResultStateSlice
import App.State.Reducers.BattleTutorialStateSlice
import App.State.Reducers.CaptchaStateSlice
import App.State.Reducers.ContextTutorialStateSlice
import App.State.Reducers.CreatorTagStateSlice
import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.EntranceStateSlice
@@ -23,7 +26,9 @@ import App.State.Reducers.LocalBattleUserStateSlice
import App.State.Reducers.LoginByPasswordStateSlice
import App.State.Reducers.LootBoxesStateSlice
import App.State.Reducers.MainQuestStateSlice
import App.State.Reducers.MigrationStateSlice
import App.State.Reducers.ModalScreenStateSlice
import App.State.Reducers.ModerationStateSlice
import App.State.Reducers.NewsStateSlice
import App.State.Reducers.NotificationByCountryStateSlice
import App.State.Reducers.NotificationsStateSlice
@@ -35,16 +40,21 @@ import App.State.Reducers.QuestCategoriesStateSlice
import App.State.Reducers.QuestsStateSlice
import App.State.Reducers.RankUpDialogStateSlice
import App.State.Reducers.ReferralEventStateSlice
import App.State.Reducers.RegisterStateSlice
import App.State.Reducers.RewardedVideosStateSlice
import App.State.Reducers.RulesUpdatedStateSlice
import App.State.Reducers.ScreenStateSlice
import App.State.Reducers.SecuritySettingsStateSlice
import App.State.Reducers.SettingsStateSlice
import App.State.Reducers.ShopStateSlice
import App.State.Reducers.SpectatorStateSlice
import App.State.Reducers.SupportUrlStateSlice
import App.State.Reducers.SystemStateSlice
import App.State.Reducers.TipsStateSlice
import App.State.Reducers.TwitchStateSlice
import App.State.Reducers.TwoStepVerificationStateSlice
import App.State.Reducers.UserStateSlice
import App.State.Reducers.UserReportsStateSlice
/**
* 原 `z6` / `TOState`:89 个切片字段,顺序与 toString 一致。
@@ -59,8 +69,8 @@ data class RootState(
val ModalScreen: ModalScreenStateSlice = ModalScreenStateSlice(),
/** 原 battleStatistics(jR / rr.fqr)。未迁移:原样透传。 */
val BattleStatistics: Any? = null,
/** 原 battleTutorial(rE / Q.sq8)。未迁移:原样透传。 */
val BattleTutorial: Any? = null,
/** 原 battleTutorial(rE / Q.sq8)。已迁移。 */
val BattleTutorial: BattleTutorialStateSlice = BattleTutorialStateSlice(),
/** 原 garage(UB / hF)。已迁移。 */
val Garage: GarageStateSlice = GarageStateSlice(),
/** 原 garagePreview(IG / fs.eru)。已迁移。 */
@@ -69,18 +79,18 @@ data class RootState(
val Challenges: Any? = null,
/** 原 loginByPassword(gD / nu.cr6)。已迁移。 */
val LoginByPassword: LoginByPasswordStateSlice = LoginByPasswordStateSlice(),
/** 原 register(Cit / cp.stg)。未迁移:原样透传。 */
val Register: Any? = null,
/** 原 migrationState($it / Uv.rtg)。未迁移:原样透传。 */
val MigrationState: Any? = null,
/** 原 register(Cit / cp.stg)。已迁移。 */
val Register: RegisterStateSlice = RegisterStateSlice(),
/** 原 migrationState($it / Uv.rtg)。已迁移。 */
val MigrationState: MigrationStateSlice = MigrationStateSlice(),
/** 原 inviteState(aD / Ke.er5)。已迁移。 */
val InviteState: InviteStateSlice = InviteStateSlice(),
/** 原 captchaState(jP / ar.iqt)。已迁移。 */
val CaptchaState: CaptchaStateSlice = CaptchaStateSlice(),
/** 原 passwordChangeState(VD / uu.pr6)。已迁移。 */
val PasswordChangeState: PasswordChangeStateSlice = PasswordChangeStateSlice(),
/** 原 system(_9 / w9().xt9)。未迁移:原样透传。 */
val System: Any? = null,
/** 原 system(_9 / w9().xt9)。已迁移。 */
val System: SystemStateSlice = SystemStateSlice(),
/** 原 quests(Frt / Yrt.otg)。已迁移。 */
val Quests: QuestsStateSlice = QuestsStateSlice(),
/** 原 chatState(LP / $r.wqv)。未迁移:原样透传。 */
@@ -179,12 +189,12 @@ data class RootState(
val Partner: Any? = null,
/** 原 entrance(hD / Bv.pth)。已迁移。 */
val Entrance: EntranceStateSlice = EntranceStateSlice(),
/** 原 antiAddiction(UC / W.iq8)。未迁移:原样透传。 */
val AntiAddiction: Any? = null,
/** 原 spectator(HA / Li.jqm)。未迁移:原样透传。 */
val Spectator: Any? = null,
/** 原 moderation(NI / Ei.oqh)。未迁移:原样透传。 */
val Moderation: Any? = null,
/** 原 antiAddiction(UC / W.iq8)。已迁移。 */
val AntiAddiction: AntiAddictionStateSlice = AntiAddictionStateSlice(),
/** 原 spectator(HA / Li.jqm)。已迁移。 */
val Spectator: SpectatorStateSlice = SpectatorStateSlice(),
/** 原 moderation(NI / Ei.oqh)。已迁移。 */
val Moderation: ModerationStateSlice = ModerationStateSlice(),
/** 原 rulesUpdated(i1 / Ch.dsm)。已迁移。 */
val RulesUpdated: RulesUpdatedStateSlice = RulesUpdatedStateSlice(),
/** 原 announcement(lC / PC().rq5)。已迁移。 */
@@ -197,8 +207,8 @@ data class RootState(
val EventGoods: EventGoodsStateSlice = EventGoodsStateSlice(),
/** 原 creatorTagState(qO / ze.er2)。已迁移。 */
val CreatorTagState: CreatorTagStateSlice = CreatorTagStateSlice(),
/** 原 twoStepVerificationState(x9 / _f.atb)。未迁移:原样透传。 */
val TwoStepVerificationState: Any? = null,
/** 原 twoStepVerificationState(x9 / _f.atb)。已迁移。 */
val TwoStepVerificationState: TwoStepVerificationStateSlice = TwoStepVerificationStateSlice(),
/** 原 rewardedVideosState(JQ / xh.zsl)。已迁移。 */
val RewardedVideosState: RewardedVideosStateSlice = RewardedVideosStateSlice(),
/** 原 twitchState(g9 / uf.gta)。已迁移。 */
@@ -207,8 +217,8 @@ data class RootState(
val Icons: IconsStateSlice = IconsStateSlice(),
/** 原 itemNotifications(_1 / A1().dso)。未迁移:原样透传。 */
val ItemNotifications: Any? = null,
/** 原 contextTutorial(Mst / Jst().tth)。未迁移:原样透传。 */
val ContextTutorial: Any? = null,
/** 原 contextTutorial(Mst / Jst().tth)。已迁移。 */
val ContextTutorial: ContextTutorialStateSlice = ContextTutorialStateSlice(),
/** 原 suppliesTutorial(? / Uw.uth)。未迁移:原样透传。 */
val SuppliesTutorial: Any? = null,
/** 原 questCategories(zZ / th.lsh)。已迁移。 */
@@ -217,8 +227,8 @@ data class RootState(
val ProBattle: ProBattleStateSlice = ProBattleStateSlice(),
/** 原 availabilityByRank(tet / $p.xth)。已迁移。 */
val AvailabilityByRank: AvailabilityByRankStateSlice = AvailabilityByRankStateSlice(),
/** 原 userReportsState(Zst / qot.zth)。未迁移:原样透传。 */
val UserReportsState: Any? = null,
/** 原 userReportsState(Zst / qot.zth)。已迁移。 */
val UserReportsState: UserReportsStateSlice = UserReportsStateSlice(),
/** 原 tankBuilderState(brt / wp.ati)。未迁移:原样透传。 */
val TankBuilderState: Any? = null,
/** 原 contractState(Ont / xv.bti)。未迁移:原样透传。 */
@@ -18,13 +18,13 @@ object RootWiring {
SliceWiring("screen", "zpu_1", "WK", "dY().ks5", "dY().ks5(t, n.zpu_1)", listOf("ClearScreenStack", "DelayNextGoTo", "DisableBack", "DisableForeground", "DisplayMainEntranceScreen", "DisplayReauthorizationMessage", "EnableBack", "EnableForeground", "FilterScreenStack", "GoTo", "PopScreen", "PressedAnyButton", "ResetScreenStackOnNextGoTo")),
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("AddModal", "CloseCurrentModal", "RemoveCurrentByName", "RemoveModal")),
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("?DR", "?HR", "?KR", "?PR", "?QR", "?UR", "?XR", "?YR", "?ZR", "?nP", "?tP", "Init", "InitBattleModeWithLevels", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "UpdateFund", "UpdateScore")),
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("?oE", "?sE", "SetFragsCount", "ShowMessage")),
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("MarkMovementStarted", "ResetBattleTutorial", "SetFragsCount", "ShowMessage")),
SliceWiring("garage", "dpv_1", "UB", "hF", "hF(t, n)", listOf("AddGarageItemCount", "AddNewItem", "AddOverdriveDescription", "AddRelatedBazooka", "AddRelatedResistanceModule", "AddToFavoriteItem", "BuyProtectionSlot", "ClearWaitServerItemMount", "GarageAddItemToDepot", "GarageApplyResistanceMount", "GarageBuyDevice", "GarageDeviceTrySetInfinityLifetime", "GarageDevicesLoadedAction", "GarageLoaded", "GarageMountItem", "GarageMountItems", "GarageRemoveDevice", "GarageRemoveDiscountForDevice", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore", "GarageResistanceUnMount", "GarageSelectAlteration", "GarageSelectItem", "GarageSelectShotSkin", "GarageSelectSkin", "GarageUnmountItem", "GarageUpdateItemCount", "GarageUpgradeItemAction", "GarageUpgradeItemConfirm", "GarageUpgradeItemCoolDownFinished", "GarageUpgradeItemCoolDownStarted", "GoTo", "HideVideoInDescription", "InitState", "RemoveFromFavoriteItem", "RemoveFromSale", "RemoveNewItem", "RemoveWaitServerItemMount", "ResetGarage", "ResetMountDelay", "SelectGarageCategory", "SetCooldownReduceEndTime", "SetGarageNotLoaded", "SetGarageObject", "SetGarageSkinsAction", "SetItemRarity", "SetItemsOnDepot", "SetItemsOnMarket", "SetMountedItems", "SetMountedShotSkinForItems", "SetMountedSkinForItems", "SetNewItems", "SetResistanceReadyToMount", "SetRestrictionMount", "ShowGarageDiscountNotification", "StartCooldownReduce", "SwitchingRowsScroll", "UpdateDiscount")),
SliceWiring("garagePreview", "epv_1", "IG", "fs.eru", "fs.eru(t, n.epv_1)", listOf("AddRequestedItem", "CaptureScreenshot", "CaptureVideo", "ClearDroneData", "ClearGrenadeData", "ClearHasGrenades", "ClearHullData", "ClearWeaponData", "InitGarageResources", "ResetItem3dModelActionCache", "SetCaptureActions", "SetDrone", "SetExpectedItemId", "SetGrenadeData", "SetHasGrenades", "SetHullData", "SetMountedItemsProperties", "SetPaint", "SetWeaponData", "ShowRequest", "UpdatePreviewSettings", "UpdateViewStyle")),
SliceWiring("challenges", "fpv_1", "CZ", "nh.hsj", "nh.hsj(t, n.fpv_1)", listOf("?LZ", "RemoveChallenge", "SetBattlePass", "SetBattlePassPackages", "SetChallenge", "SetRewardForDialog", "SetTakenBattlePassRewardIndex", "SetTakenFreeRewardIndex", "UpdateStars")),
SliceWiring("loginByPassword", "gpv_1", "gD", "nu.cr6", "nu.cr6(t, n.gpv_1)", listOf("?ND", "?PD", "?RD", "IncorrectLogin2FA", "RequestsNumber2FA", "SetLogin", "SetPassword", "SetRememberMe", "SetStatus")),
SliceWiring("register", "hpv_1", "Cit", "cp.stg", "cp.stg(t, n.hpv_1)", listOf("?Bit", "?Dit", "?Fit", "?Git", "?Hit", "?Lit", "?Mit", "?Uit", "?_rt", "?hrt", "EnteredUidIsBusy", "ForceSetUid", "ReceiveOAuthUri", "SetOpenedPassword", "SetPassword", "SetPasswordRestrictions", "SetUid", "SocialNetworkAvailability", "SocialNetworkClientIds")),
SliceWiring("migrationState", "ipv_1", "\$it", "Uv.rtg", "Uv.rtg(t, n.ipv_1)", listOf("?Bit", "?Dit", "?Fit", "?Git", "?Hit", "?Lit", "?_rt", "EnteredUidIsBusy", "ForceSetUid", "SetPassword", "SetUid")),
SliceWiring("register", "hpv_1", "Cit", "cp.stg", "cp.stg(t, n.hpv_1)", listOf("DisableRegister", "EnteredUidIsBusy", "ForceSetUid", "ReceiveOAuthUri", "RegisterFailed", "RegisterPasswordCorrect", "RegisterPasswordIncorrect", "ResetAuthenticationForm", "ResetRegisterStatus", "ResetUidStatus", "SetOpenedPassword", "SetPassword", "SetPasswordRestrictions", "SetUid", "SocialNetworkAvailability", "SocialNetworkClientIds", "UidIsFree", "UidIsIncorrect", "UidTooShort")),
SliceWiring("migrationState", "ipv_1", "\$it", "Uv.rtg", "Uv.rtg(t, n.ipv_1)", listOf("EnteredUidIsBusy", "ForceSetUid", "RegisterFailed", "RegisterPasswordIncorrect", "ResetAuthenticationForm", "ResetUidStatus", "SetPassword", "SetUid", "UidIsFree", "UidIsIncorrect", "UidTooShort")),
SliceWiring("inviteState", "jpv_1", "aD", "Ke.er5", "Ke.er5(t, n.jpv_1)", listOf("ApplyInvite", "SetInviteNeed", "SetInviteStatus")),
SliceWiring("captchaState", "kpv_1", "jP", "ar.iqt", "ar.iqt(t, n.kpv_1)", listOf("AddCaptchaLocationToReset", "AddVerifiedCaptchaLocation", "LocationsUpdated", "RemoveCaptchaLocationToReset", "RemoveVerifiedCaptchaLocation", "SetLocations", "ShowFakeCaptcha")),
SliceWiring("passwordChangeState", "lpv_1", "VD", "uu.pr6", "uu.pr6(t, n.lpv_1)", listOf("SetPasswordChangeHash", "ShowEmailAndPasswordChangeDialog")),
@@ -78,9 +78,9 @@ object RootWiring {
SliceWiring("launchParams", "hpx_1", "?", "(pass-through)", "n.hpx_1", listOf()),
SliceWiring("partner", "ipx_1", "UJ", "(inline@partner)", "(function (t, n) { var i; if (t instanceof VJ) i = n.osf(t.rsf_1, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ssf_1); else if (t instanceof nZ) { var r = t.bsg_1, e = t.csg_1, u = t.dsg_1, s = t.esg_1, o = t.fsg_1, _ = t.gsg_1; i = n.osf(cd, cd, cd, r, e, u, cd, cd, o, _, s); } else i = t instanceof iZ ? n.osf(cd, t.hsg_1) : t instanceof uZ ? n.osf(cd, cd, t.ksg_1) : t instanceof eZ ? n.osf(cd, cd, cd, cd, cd, cd, cd, t.jsg_1) : t instanceof _Z ? n.osf(cd, cd, cd, cd, cd, cd, t.osg_1) : t instanceof X …", listOf("?JJ", "?_Z", "?hZ", "?tZ", "InitPartnerInviteAction", "InitSocialNetworkParameters", "SetLoginParams", "SetLoginStatusForBinding", "SetPartnerId", "SetPartnerParameters", "SetPartnerReady")),
SliceWiring("entrance", "jpx_1", "hD", "Bv.pth", "Bv.pth(t, n.jpx_1)", listOf("HideSocialNetworks", "SetAgeWarnShow", "SetAuthorizedByChinaSdkToken", "SetClassic", "SetControlsForChina", "SetRunningInChinaDataCenter", "SetRunningInChinaPartners")),
SliceWiring("antiAddiction", "kpx_1", "UC", "W.iq8", "W.iq8(t, n.kpx_1)", listOf("?FC", "?XC", "?YC", "SetRealNameAndIdNumber", "SetValidateAntiAddictionUserDataStatus")),
SliceWiring("spectator", "lpx_1", "HA", "Li.jqm", "Li.jqm(t, n.lpx_1)", listOf("?JA", "?XA", "?YA", "?ZA", "SetSpectator", "UpdateHudLayout")),
SliceWiring("moderation", "mpx_1", "NI", "Ei.oqh", "Ei.oqh(t, n.mpx_1)", listOf("?FI", "?GI", "?JI", "?KI", "?QI", "?UI", "?WI", "?XI", "?YI", "?ZI", "?nA", "?tA", "?uA")),
SliceWiring("antiAddiction", "kpx_1", "UC", "W.iq8", "W.iq8(t, n.kpx_1)", listOf("EnableRealNameCheck", "RequireUserIdVerification", "SetRealNameAndIdNumber", "SetValidateAntiAddictionUserDataStatus", "UpdatePlayingTime")),
SliceWiring("spectator", "lpx_1", "HA", "Li.jqm", "Li.jqm(t, n.lpx_1)", listOf("AddSpectator", "RemoveSpectator", "SetSpectator", "SetSpectators", "SetSpectatorsListVisible", "UpdateHudLayout")),
SliceWiring("moderation", "mpx_1", "NI", "Ei.oqh", "Ei.oqh(t, n.mpx_1)", listOf("AddSelectedUser", "AddSelectedUserFromChat", "ClearSelectedUsers", "HideBattleInfo", "HideBlockUserMenu", "HideModerationSystemMessage", "RemoveSelectedUser", "ResetSelectedUsers", "SetModerationUrlTemplates", "SetModeratorModes", "ShowBattleInfo", "ShowBlockUserMenu", "ShowModerationSystemMessage")),
SliceWiring("rulesUpdated", "npx_1", "i1", "Ch.dsm", "Ch.dsm(t, n.npx_1)", listOf("UpdateRules")),
SliceWiring("announcement", "opx_1", "lC", "PC().rq5", "PC().rq5(t, n.opx_1)", listOf("DisableAnnouncement", "SetAnnouncementItems", "SetAnnouncementModelLoaded", "SetSkipNextPromotedAnnounce")),
SliceWiring("notificationByCountry", "ppx_1", "gJ", "R_.ssd", "R_.ssd(t, n.ppx_1)", listOf("Init", "MarkHomeScreenDialogShown")),
@@ -106,7 +106,7 @@ object RootWiring {
)
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState")
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
@@ -9,13 +9,15 @@
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
// ⑧ 全目录动作类都能按 KClass 路由
// ⑨~㊿/51-53 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// ⑨~㊿/51-63 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings /
// AccountSettings / ProBattle / Language / Announcement / News / Notifications / Quests /
// LobbyLoader / PurchaseDialogState / PromoCode / SupportUrl / RulesUpdated / NotificationByCountry / AvailabilityByRank /
// Icons / QuestCategories / MainQuest / SecuritySettings / ReferralEvent /
// TipsState / RankUpDialogState / BattleResult / CaptchaState / ModalScreen / DroppableGold / EventGoods /
// CreatorTagState / RewardedVideosState / PasswordChangeState;
// CreatorTagState / RewardedVideosState / PasswordChangeState /
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
// Moderation / MigrationState / Register / UserReports;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -440,6 +442,98 @@ import App.State.SetPasswordChangeHash
import App.State.ShowEmailAndPasswordChangeDialog
import App.State.Reducers.PasswordChangeStateSlice
import App.State.Reducers.ReducePasswordChange
import App.State.DoShowMessage
import App.State.SetFatalError
import App.State.Reducers.SystemStateSlice
import App.State.Reducers.ReduceSystem
import App.State.LoadCurrentElement
import App.State.MarkElementAsViewed
import App.State.SetLoadedElements
import App.State.SetMissionToElement
import App.State.SetUnViewedElements
import App.State.Reducers.ContextTutorialStateSlice
import App.State.Reducers.ReduceContextTutorial
import App.State.SetFragsCount
import App.State.ShowMessage
import App.State.Reducers.BattleTutorialStateSlice
import App.State.Reducers.MarkMovementStarted
import App.State.Reducers.ReduceBattleTutorial
import App.State.Reducers.ResetBattleTutorial
import App.State.SetSpectator
import App.State.UpdateHudLayout
import App.State.SpectatorInfo
import App.State.Reducers.AddSpectator
import App.State.Reducers.ReduceSpectator
import App.State.Reducers.RemoveSpectator
import App.State.Reducers.SetSpectators
import App.State.Reducers.SetSpectatorsListVisible
import App.State.Reducers.SpectatorStateSlice
import App.State.ActivationStatus2FA
import App.State.Enable2FaRequest
import App.State.SetSecretAndQRCode
import App.State.SetUse2FA
import App.State.VerificationStatus
import App.State.Reducers.ReduceTwoStepVerification
import App.State.Reducers.TwoStepVerificationStateSlice
import App.State.SetRealNameAndIdNumber
import App.State.SetValidateAntiAddictionUserDataStatus
import App.State.Reducers.AntiAddictionStateSlice
import App.State.Reducers.EnableRealNameCheck
import App.State.Reducers.ReduceAntiAddiction
import App.State.Reducers.RequireUserIdVerification
import App.State.Reducers.UpdatePlayingTime
import App.State.Reducers.AddSelectedUser
import App.State.Reducers.AddSelectedUserFromChat
import App.State.Reducers.ClearSelectedUsers
import App.State.Reducers.HideBattleInfo
import App.State.Reducers.HideBlockUserMenu
import App.State.Reducers.HideModerationSystemMessage
import App.State.Reducers.ModerationStateSlice
import App.State.Reducers.ModerationUserRecord
import App.State.Reducers.ReduceModeration
import App.State.Reducers.RemoveSelectedUser
import App.State.Reducers.ResetSelectedUsers
import App.State.Reducers.SetModerationUrlTemplates
import App.State.Reducers.SetModeratorModes
import App.State.Reducers.ShowBattleInfo
import App.State.Reducers.ShowBlockUserMenu
import App.State.Reducers.ShowModerationSystemMessage
import App.State.EnteredUidIsBusy
import App.State.ForceSetUid
import App.State.SetUid
import App.State.ReceiveOAuthUri
import App.State.SetOpenedPassword
import App.State.SetPasswordRestrictions
import App.State.SocialNetworkAvailability
import App.State.SocialNetworkClientIds
import App.State.Reducers.DisableRegister
import App.State.Reducers.MigrationLoginStatus
import App.State.Reducers.MigrationRegisterStatus
import App.State.Reducers.MigrationStateSlice
import App.State.Reducers.ReduceMigrationState
import App.State.Reducers.ReduceRegister
import App.State.Reducers.RegisterFailed
import App.State.Reducers.RegisterPasswordCorrect
import App.State.Reducers.RegisterPasswordIncorrect
import App.State.Reducers.RegisterStateSlice
import App.State.Reducers.RegistrationStatus
import App.State.Reducers.ResetAuthenticationForm
import App.State.Reducers.ResetRegisterStatus
import App.State.Reducers.ResetUidStatus
import App.State.Reducers.UidIsFree
import App.State.Reducers.UidIsIncorrect
import App.State.Reducers.UidStatus
import App.State.Reducers.UidTooShort
import App.State.InitUserReports
import App.State.ReportBattleChatMessage
import App.State.ReportLobbyChatMessage
import App.State.ReportProBattleName
import App.State.SetAvailableGameReports
import App.State.SetAvailableReports
import App.State.Reducers.ReduceUserReports
import App.State.Reducers.ReportEntry
import App.State.Reducers.ReportKind
import App.State.Reducers.UserReportsStateSlice
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -867,7 +961,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 43 个已迁移切片(42 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 53 个已迁移切片(52 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -931,7 +1025,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 43 个已迁移字段,其余 46 个待迁",
"根:迁移表 = 53 个已迁移字段,其余 36 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -945,8 +1039,11 @@ object StateCheck {
"tipsState", "rankUpDialogState", "battleResult", "captchaState",
"modalScreen", "droppableGold", "eventGoods", "creatorTagState",
"rewardedVideosState", "passwordChangeState",
"system", "contextTutorial", "battleTutorial", "spectator",
"twoStepVerificationState", "antiAddiction", "moderation",
"migrationState", "register", "userReportsState",
) &&
RootWiring.Unmigrated.size == 46
RootWiring.Unmigrated.size == 36
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -1002,7 +1099,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 43/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 53/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -4120,6 +4217,604 @@ object StateCheck {
)
}
// 54 切片 reducer:System(_9 / w9().xt9,3 字段;2 个动作)
run {
val S0 = SystemStateSlice()
Check(
"切片 System:默认值完整",
S0.Message == null && !S0.Restart && S0.FatalError == null
)
val S1 = ReduceSystem(S0, DoShowMessage("m", true))
Check(
"切片 System:DoShowMessage 写 message/restart",
S1.Message == "m" && S1.Restart && S1.FatalError == null
)
val S2 = ReduceSystem(S1, SetFatalError("boom"))
Check(
"切片 System:SetFatalError 写 fatalError",
S2.FatalError == "boom" && S2.Message == "m"
)
Check("切片 System:无关动作不改状态", ReduceSystem(S2, AddFrags(1)) == S2)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetFatalError("x"))
Check(
"根:SetFatalError 只动 system",
Root1.System.FatalError == "x" && Root1.ContextTutorial == Root0.ContextTutorial
)
val W = RootWiring.ByField("system")!!
Check(
"根:system 委派到 _9 / w9().xt9",
W.SliceClass == "_9" && W.Reducer == "w9().xt9" &&
W.Actions.toSet() == setOf("DoShowMessage", "SetFatalError")
)
}
// 55 切片 reducer:ContextTutorial(Mst / Jst().tth,4 字段;5 个动作)
run {
val S0 = ContextTutorialStateSlice()
Check(
"切片 ContextTutorial:默认空表空 Map",
S0.CurrentElement == null && S0.LoadedElements.isEmpty() &&
S0.UnViewedElements.isEmpty() && S0.MissionToElement.isEmpty()
)
val S1 = ReduceContextTutorial(S0, LoadCurrentElement("e1"))
Check("切片 ContextTutorial:LoadCurrentElement 写当前元素", S1.CurrentElement == "e1")
val S2 = ReduceContextTutorial(
ReduceContextTutorial(S1, SetUnViewedElements(listOf("a", "b"))),
MarkElementAsViewed("a"),
)
Check(
"切片 ContextTutorial:MarkElementAsViewed 从待看列表删一个",
S2.UnViewedElements == listOf<Any?>("b")
)
val S3 = ReduceContextTutorial(
ReduceContextTutorial(S2, SetLoadedElements(mapOf<Any?, Any?>("k" to 1))),
SetMissionToElement(mapOf("m" to "e")),
)
Check(
"切片 ContextTutorial:loadedElements / missionToElement 整体替换",
S3.LoadedElements == mapOf<Any?, Any?>("k" to 1) &&
S3.MissionToElement == mapOf("m" to "e")
)
Check(
"切片 ContextTutorial:无关动作不改状态",
ReduceContextTutorial(S3, AddFrags(1)) == S3
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, LoadCurrentElement("z"))
Check(
"根:LoadCurrentElement 只动 contextTutorial",
Root1.ContextTutorial.CurrentElement == "z" && Root1.System == Root0.System
)
val W = RootWiring.ByField("contextTutorial")!!
Check(
"根:contextTutorial 委派到 Mst / Jst().tth 且 5 个动作",
W.SliceClass == "Mst" && W.Reducer == "Jst().tth" &&
W.Actions.toSet() == setOf(
"LoadCurrentElement", "SetUnViewedElements", "MarkElementAsViewed",
"SetLoadedElements", "SetMissionToElement",
)
)
}
// 56 切片 reducer:BattleTutorial(rE / Q.sq8,3 字段;4 个动作)
run {
val S0 = BattleTutorialStateSlice()
Check(
"切片 BattleTutorial:默认值完整",
S0.Message == null && S0.FragsCount == 0 && !S0.MovementHasStarted
)
val S1 = ReduceBattleTutorial(S0, ShowMessage("m"))
Check("切片 BattleTutorial:ShowMessage 写 message", S1.Message == "m")
Check(
"切片 BattleTutorial:SetFragsCount 写击杀数",
ReduceBattleTutorial(S1, SetFragsCount(3)).FragsCount == 3
)
val S2 = ReduceBattleTutorial(S1, MarkMovementStarted)
Check("切片 BattleTutorial:MarkMovementStarted 置位", S2.MovementHasStarted)
Check(
"切片 BattleTutorial:ResetBattleTutorial 整片复位",
ReduceBattleTutorial(S2, ResetBattleTutorial) == BattleTutorialStateSlice()
)
Check(
"切片 BattleTutorial:无关动作不改状态",
ReduceBattleTutorial(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, MarkMovementStarted)
Check(
"根:MarkMovementStarted 只动 battleTutorial",
Root1.BattleTutorial.MovementHasStarted && Root1.Spectator == Root0.Spectator
)
val W = RootWiring.ByField("battleTutorial")!!
Check(
"根:battleTutorial 委派到 rE / Q.sq8 且 4 个动作",
W.SliceClass == "rE" && W.Reducer == "Q.sq8" &&
W.Actions.toSet() == setOf(
"ShowMessage", "SetFragsCount", "MarkMovementStarted", "ResetBattleTutorial",
)
)
}
// 57 切片 reducer:Spectator(HA / Li.jqm,4 字段;6 个动作)
run {
val S0 = SpectatorStateSlice()
Check(
"切片 Spectator:默认非观战、列表可见",
!S0.IsSpectator && S0.Spectators.isEmpty() && S0.SpectatorsListVisible
)
val S1 = ReduceSpectator(S0, SetSpectator(true))
Check("切片 Spectator:SetSpectator 写布尔", S1.IsSpectator)
val S2 = ReduceSpectator(S1, AddSpectator(1L, "uid1", true, false))
Check(
"切片 Spectator:AddSpectator 追加 SpectatorInfo",
S2.Spectators.size == 1 &&
(S2.Spectators[0] as SpectatorInfo).userId == 1L
)
val S3 = ReduceSpectator(S2, AddSpectator(2L, "uid2", false, false))
Check("切片 Spectator:第二个观战者入列", S3.Spectators.size == 2)
Check(
"切片 Spectator:RemoveSpectator 按 userId 删",
ReduceSpectator(S3, RemoveSpectator(1L)).Spectators.size == 1
)
Check(
"切片 Spectator:SetSpectators 整表替换",
ReduceSpectator(S3, SetSpectators(listOf("x"))).Spectators == listOf<Any?>("x")
)
Check(
"切片 Spectator:SetSpectatorsListVisible 写可见性",
!ReduceSpectator(S3, SetSpectatorsListVisible(false)).SpectatorsListVisible
)
Check(
"切片 Spectator:UpdateHudLayout 写布局",
ReduceSpectator(S3, UpdateHudLayout("hud")).SpectatorHudLayout == "hud"
)
Check("切片 Spectator:无关动作不改状态", ReduceSpectator(S3, AddFrags(1)) == S3)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetSpectator(true))
Check(
"根:SetSpectator 只动 spectator",
Root1.Spectator.IsSpectator && Root1.BattleTutorial == Root0.BattleTutorial
)
val W = RootWiring.ByField("spectator")!!
Check(
"根:spectator 委派到 HA / Li.jqm 且 6 个动作",
W.SliceClass == "HA" && W.Reducer == "Li.jqm" &&
W.Actions.toSet() == setOf(
"SetSpectator", "UpdateHudLayout", "SetSpectators", "AddSpectator",
"RemoveSpectator", "SetSpectatorsListVisible",
)
)
}
// 58 切片 reducer:TwoStepVerification(x9 / _f.atb,6 字段;5 个动作)
run {
val S0 = TwoStepVerificationStateSlice()
Check(
"切片 TwoStepVerification:默认值完整",
!S0.IsUse2FA && !S0.IsActivated2FA && S0.QrCodeDataURI == null &&
S0.SharedSecret == null && S0.VerificationStatus == null &&
!S0.Enable2FaRequest
)
val S1 = ReduceTwoStepVerification(S0, SetUse2FA(true))
Check("切片 TwoStepVerification:SetUse2FA 写开关", S1.IsUse2FA)
val S2 = ReduceTwoStepVerification(S1, SetSecretAndQRCode("secret", "data"))
Check(
"切片 TwoStepVerification:SetSecretAndQRCode 同时写两字段",
S2.SharedSecret == "secret" && S2.QrCodeDataURI == "data"
)
Check(
"切片 TwoStepVerification:VerificationStatus(false) 写失败",
ReduceTwoStepVerification(S2, VerificationStatus(false)).VerificationStatus == false
)
Check(
"切片 TwoStepVerification:VerificationStatus(true) 视为已启用",
ReduceTwoStepVerification(S2, VerificationStatus(true)).IsActivated2FA
)
Check(
"切片 TwoStepVerification:VerificationStatus(null) 清空状态",
ReduceTwoStepVerification(
ReduceTwoStepVerification(S2, VerificationStatus(false)),
VerificationStatus(null),
).VerificationStatus == null
)
Check(
"切片 TwoStepVerification:ActivationStatus2FA / Enable2FaRequest",
!ReduceTwoStepVerification(S2, ActivationStatus2FA(false)).IsActivated2FA &&
ReduceTwoStepVerification(S2, Enable2FaRequest(true)).Enable2FaRequest
)
Check(
"切片 TwoStepVerification:无关动作不改状态",
ReduceTwoStepVerification(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetUse2FA(true))
Check(
"根:SetUse2FA 只动 twoStepVerificationState",
Root1.TwoStepVerificationState.IsUse2FA && Root1.Spectator == Root0.Spectator
)
val W = RootWiring.ByField("twoStepVerificationState")!!
Check(
"根:twoStepVerificationState 委派到 x9 / _f.atb 且 5 个动作",
W.SliceClass == "x9" && W.Reducer == "_f.atb" &&
W.Actions.toSet() == setOf(
"SetUse2FA", "ActivationStatus2FA", "SetSecretAndQRCode",
"VerificationStatus", "Enable2FaRequest",
)
)
}
// 59 切片 reducer:AntiAddiction(UC / W.iq8,8 字段;5 个动作)
run {
val S0 = AntiAddictionStateSlice()
Check(
"切片 AntiAddiction:默认值完整",
!S0.IsEnableRealNameCheck && !S0.UnknownUserId && S0.UrlForEnterUserId == null &&
S0.PlayingTimeInMinutes == 0 && S0.AlertMessage == "" &&
S0.IdNumber == "" && S0.RealName == "" && S0.ValidateStatus == null
)
val S1 = ReduceAntiAddiction(S0, EnableRealNameCheck)
Check("切片 AntiAddiction:EnableRealNameCheck 置位", S1.IsEnableRealNameCheck)
val S2 = ReduceAntiAddiction(S1, RequireUserIdVerification("https://u"))
Check(
"切片 AntiAddiction:RequireUserIdVerification 写 unknown+url",
S2.UnknownUserId && S2.UrlForEnterUserId == "https://u"
)
val S3 = ReduceAntiAddiction(S2, UpdatePlayingTime(180, "休息"))
Check(
"切片 AntiAddiction:UpdatePlayingTime 写时长与提示",
S3.PlayingTimeInMinutes == 180 && S3.AlertMessage == "休息"
)
val S4 = ReduceAntiAddiction(S3, SetRealNameAndIdNumber("张三", "110"))
Check(
"切片 AntiAddiction:SetRealNameAndIdNumber 写实名与证件号",
S4.RealName == "张三" && S4.IdNumber == "110"
)
Check(
"切片 AntiAddiction:实名参数为 null 时保留旧值",
ReduceAntiAddiction(S4, SetRealNameAndIdNumber(null, null)) == S4
)
Check(
"切片 AntiAddiction:SetValidateAntiAddictionUserDataStatus 写状态",
ReduceAntiAddiction(S4, SetValidateAntiAddictionUserDataStatus("ok"))
.ValidateStatus == "ok"
)
Check("切片 AntiAddiction:无关动作不改状态", ReduceAntiAddiction(S4, AddFrags(1)) == S4)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, EnableRealNameCheck)
Check(
"根:EnableRealNameCheck 只动 antiAddiction",
Root1.AntiAddiction.IsEnableRealNameCheck && Root1.Moderation == Root0.Moderation
)
val W = RootWiring.ByField("antiAddiction")!!
Check(
"根:antiAddiction 委派到 UC / W.iq8 且 5 个动作",
W.SliceClass == "UC" && W.Reducer == "W.iq8" &&
W.Actions.toSet() == setOf(
"EnableRealNameCheck", "RequireUserIdVerification", "UpdatePlayingTime",
"SetRealNameAndIdNumber", "SetValidateAntiAddictionUserDataStatus",
)
)
}
// 60 切片 reducer:Moderation(NI / Ei.oqh,11 字段;13 个动作)
run {
val S0 = ModerationStateSlice()
Check(
"切片 Moderation:默认值完整",
!S0.ModeratorMode && !S0.BattleModeratorMode && !S0.EventOrganizerMode &&
S0.ModerationToolBattleUrlTemplate == "" &&
S0.ModerationToolUserUrlTemplate == "" && S0.BattleInfo == null &&
!S0.ForceShowBattleInfo && S0.SystemMessage == "" &&
!S0.ForceShowSystemMessage && !S0.ForceShowBlockUserMenu &&
S0.SelectedUsers.isEmpty()
)
val S1 = ReduceModeration(S0, SetModeratorModes(true, false, true))
Check(
"切片 Moderation:SetModeratorModes 写三个开关",
S1.ModeratorMode && !S1.BattleModeratorMode && S1.EventOrganizerMode
)
Check(
"切片 Moderation:SetModerationUrlTemplates 写两个模板",
ReduceModeration(S1, SetModerationUrlTemplates("b", "u"))
.ModerationToolBattleUrlTemplate == "b"
)
val S2 = ReduceModeration(S1, ShowBattleInfo("info"))
Check(
"切片 Moderation:ShowBattleInfo 写面板并强制显示",
S2.BattleInfo == "info" && S2.ForceShowBattleInfo
)
Check(
"切片 Moderation:HideBattleInfo 取消强制显示",
!ReduceModeration(S2, HideBattleInfo).ForceShowBattleInfo
)
val S3 = ReduceModeration(
ReduceModeration(S2, AddSelectedUser(1L, 5, "a")),
AddSelectedUserFromChat(1L, 9, "a2"),
)
Check(
"切片 Moderation:选中用户按 userId 去重后追加",
S3.SelectedUsers.size == 1 &&
(S3.SelectedUsers[0] as ModerationUserRecord).userRank == 9
)
Check(
"切片 Moderation:RemoveSelectedUser 按 userId 删",
ReduceModeration(
ReduceModeration(S3, AddSelectedUser(2L, 1, "b")),
RemoveSelectedUser(1L),
).SelectedUsers.size == 1
)
Check(
"切片 Moderation:ClearSelectedUsers / ResetSelectedUsers 清空",
ReduceModeration(S3, ClearSelectedUsers).SelectedUsers.isEmpty() &&
ReduceModeration(S3, ResetSelectedUsers).SelectedUsers.isEmpty()
)
val S4 = ReduceModeration(S3, ShowModerationSystemMessage("hello"))
Check(
"切片 Moderation:ShowModerationSystemMessage 写消息并强制显示",
S4.SystemMessage == "hello" && S4.ForceShowSystemMessage
)
Check(
"切片 Moderation:HideModerationSystemMessage 取消强制显示",
!ReduceModeration(S4, HideModerationSystemMessage).ForceShowSystemMessage
)
Check(
"切片 Moderation:ShowBlockUserMenu / HideBlockUserMenu 切换",
ReduceModeration(S4, ShowBlockUserMenu).ForceShowBlockUserMenu &&
!ReduceModeration(S4, HideBlockUserMenu).ForceShowBlockUserMenu
)
Check("切片 Moderation:无关动作不改状态", ReduceModeration(S4, AddFrags(1)) == S4)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ShowBlockUserMenu)
Check(
"根:ShowBlockUserMenu 只动 moderation",
Root1.Moderation.ForceShowBlockUserMenu && Root1.AntiAddiction == Root0.AntiAddiction
)
val W = RootWiring.ByField("moderation")!!
Check(
"根:moderation 委派到 NI / Ei.oqh 且 13 个动作",
W.SliceClass == "NI" && W.Reducer == "Ei.oqh" && W.Actions.size == 13
)
}
// 61 切片 reducer:MigrationState($it / Uv.rtg,7 字段;11 个动作)
run {
val S0 = MigrationStateSlice()
Check(
"切片 MigrationState:默认值完整",
!S0.RequestLogin && S0.Login == "" && S0.ForceLogin == null &&
S0.Password == "" &&
S0.LoginStatus == MigrationLoginStatus.UNKNOWN &&
S0.RegisterStatus == MigrationRegisterStatus.UNKNOWN &&
S0.AdvisedUids.isEmpty()
)
val S1 = ReduceMigrationState(S0, SetUid("alice"))
Check(
"切片 MigrationState:SetUid 写 login 并复位状态",
S1.Login == "alice" &&
S1.LoginStatus == MigrationLoginStatus.UNKNOWN &&
S1.RegisterStatus == MigrationRegisterStatus.UNKNOWN
)
Check(
"切片 MigrationState:ForceSetUid 写 forceLogin",
ReduceMigrationState(S1, ForceSetUid("forced")).ForceLogin == "forced"
)
Check(
"切片 MigrationState:SetPassword 写 password",
ReduceMigrationState(S1, SetPassword("pwd")).Password == "pwd"
)
Check(
"切片 MigrationState:UidIsFree/UidIsIncorrect/UidTooShort 写 loginStatus",
ReduceMigrationState(S1, UidIsFree).LoginStatus == MigrationLoginStatus.FREE &&
ReduceMigrationState(S1, UidIsIncorrect).LoginStatus ==
MigrationLoginStatus.INCORRECT &&
ReduceMigrationState(S1, UidTooShort).LoginStatus ==
MigrationLoginStatus.SHORT_NAME
)
val S2 = ReduceMigrationState(S1, EnteredUidIsBusy(listOf("u1")))
Check(
"切片 MigrationState:EnteredUidIsBusy 写 BUSY 与 advisedUids",
S2.LoginStatus == MigrationLoginStatus.BUSY && S2.AdvisedUids == listOf<Any?>("u1")
)
Check(
"切片 MigrationState:注册状态两支",
ReduceMigrationState(S1, RegisterPasswordIncorrect).RegisterStatus ==
MigrationRegisterStatus.PASSWORD_INCORRECT &&
ReduceMigrationState(S1, RegisterFailed).RegisterStatus ==
MigrationRegisterStatus.FAILED
)
Check(
"切片 MigrationState:ResetUidStatus / ResetAuthenticationForm",
ReduceMigrationState(S2, ResetUidStatus).LoginStatus ==
MigrationLoginStatus.UNKNOWN &&
ReduceMigrationState(S2, ResetAuthenticationForm).Login == ""
)
Check(
"切片 MigrationState:无关动作不改状态",
ReduceMigrationState(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetUid("bob"))
Check(
"根:SetUid 同时动 migrationState 与 register",
Root1.MigrationState.Login == "bob" && Root1.Register.Login == "bob" &&
Root1.System == Root0.System
)
val W = RootWiring.ByField("migrationState")!!
Check(
"根:migrationState 委派到 \$it / Uv.rtg 且 11 个动作",
W.SliceClass == "\$it" && W.Reducer == "Uv.rtg" && W.Actions.size == 11
)
}
// 62 切片 reducer:Register(Cit / cp.stg,16 字段;19 个动作)
run {
val S0 = RegisterStateSlice()
Check(
"切片 Register:默认值完整",
!S0.IsDisabled && S0.Login == "" && S0.ForceLogin == null && S0.Password == "" &&
S0.UidValidationStatus == UidStatus.UNKNOWN &&
S0.RegisterStatus == RegistrationStatus.UNKNOWN &&
S0.AdvisedUids.isEmpty() && S0.SocialNetworkAvailability.isEmpty() &&
S0.SocialNetworkClientIds == emptyMap<String, Any?>() &&
S0.SocialNetworkOAuthData == null && S0.MinPasswordLength == 6 &&
S0.MaxPasswordLength == 20 && !S0.PasswordNeedLower &&
!S0.PasswordNeedUpper && !S0.PasswordNeedDigit && S0.OpenedPassword
)
val S1 = ReduceRegister(S0, DisableRegister)
Check("切片 Register:DisableRegister 置灰", S1.IsDisabled)
val S2 = ReduceRegister(S1, SetUid("alice"))
Check(
"切片 Register:SetUid 写 login 并复位 uid 状态",
S2.Login == "alice" && S2.UidValidationStatus == UidStatus.UNKNOWN
)
Check(
"切片 Register:原状态为 INCORRECT 时保留 registerStatus,否则复位",
ReduceRegister(
ReduceRegister(
ReduceRegister(S2, RegisterFailed),
UidIsIncorrect,
),
SetUid("alice2"),
).RegisterStatus == RegistrationStatus.FAILED &&
ReduceRegister(
ReduceRegister(
ReduceRegister(S2, RegisterFailed),
ResetUidStatus,
),
SetUid("alice2"),
).RegisterStatus == RegistrationStatus.UNKNOWN
)
val S3 = ReduceRegister(S2, EnteredUidIsBusy(listOf("u1")))
Check(
"切片 Register:EnteredUidIsBusy 写 BUSY + advisedUids",
S3.UidValidationStatus == UidStatus.BUSY && S3.AdvisedUids == listOf<Any?>("u1")
)
Check(
"切片 Register:uid 三个状态臂",
ReduceRegister(S2, UidIsFree).UidValidationStatus == UidStatus.FREE &&
ReduceRegister(S2, UidIsIncorrect).UidValidationStatus == UidStatus.INCORRECT &&
ReduceRegister(S2, UidTooShort).UidValidationStatus == UidStatus.SHORT_NAME
)
Check(
"切片 Register:注册状态四臂",
ReduceRegister(S2, RegisterPasswordCorrect).RegisterStatus ==
RegistrationStatus.PASSWORD_CORRECT &&
ReduceRegister(S2, RegisterPasswordIncorrect).RegisterStatus ==
RegistrationStatus.PASSWORD_INCORRECT &&
ReduceRegister(S2, RegisterFailed).RegisterStatus == RegistrationStatus.FAILED &&
ReduceRegister(S2, ResetRegisterStatus).RegisterStatus ==
RegistrationStatus.UNKNOWN
)
val S4 = ReduceRegister(
ReduceRegister(S2, SetPasswordRestrictions(8, 32, true, false, true)),
SetOpenedPassword(false),
)
Check(
"切片 Register:密码规则与展开开关",
S4.MinPasswordLength == 8 && S4.MaxPasswordLength == 32 &&
S4.PasswordNeedLower && !S4.PasswordNeedUpper && S4.PasswordNeedDigit &&
!S4.OpenedPassword
)
Check(
"切片 Register:社媒三支",
ReduceRegister(S2, SocialNetworkAvailability(mapOf("vk" to true)))
.SocialNetworkAvailability == mapOf("vk" to true) &&
ReduceRegister(S2, SocialNetworkClientIds("cid"))
.SocialNetworkClientIds == "cid" &&
ReduceRegister(S2, ReceiveOAuthUri("oauth"))
.SocialNetworkOAuthData == "oauth"
)
run {
val R = ReduceRegister(S3, ResetAuthenticationForm)
Check(
"切片 Register:ResetAuthenticationForm 清 login 与两个状态",
R.Login == "" && R.UidValidationStatus == UidStatus.UNKNOWN &&
R.RegisterStatus == RegistrationStatus.UNKNOWN &&
R.AdvisedUids == S3.AdvisedUids
)
}
Check("切片 Register:无关动作不改状态", ReduceRegister(S4, AddFrags(1)) == S4)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, RegisterFailed)
Check(
"根:RegisterFailed 同时动 migrationState 与 register",
Root1.Register.RegisterStatus == RegistrationStatus.FAILED &&
Root1.MigrationState.RegisterStatus ==
MigrationRegisterStatus.FAILED
)
val W = RootWiring.ByField("register")!!
Check(
"根:register 委派到 Cit / cp.stg 且 19 个动作",
W.SliceClass == "Cit" && W.Reducer == "cp.stg" && W.Actions.size == 19
)
}
// 63 切片 reducer:UserReports(Zst / qot.zth,5 字段;6 个动作)
run {
val S0 = UserReportsStateSlice()
Check(
"切片 UserReports:默认值完整",
!S0.IsEnabled && !S0.HelpdeskTicketsEnabled && S0.AvailableReports == 0 &&
S0.AvailableGameReports == 0 && S0.Reports.isEmpty()
)
val S1 = ReduceUserReports(S0, InitUserReports(true, true, 5, 3))
Check(
"切片 UserReports:InitUserReports 写四个开关/额度",
S1.IsEnabled && S1.HelpdeskTicketsEnabled &&
S1.AvailableReports == 5 && S1.AvailableGameReports == 3
)
Check(
"切片 UserReports:SetAvailableReports / SetAvailableGameReports",
ReduceUserReports(S1, SetAvailableReports(7)).AvailableReports == 7 &&
ReduceUserReports(S1, SetAvailableGameReports(9)).AvailableGameReports == 9
)
val S2 = ReduceUserReports(S1, ReportProBattleName("b1"))
Check(
"切片 UserReports:ReportProBattleName 追加 BATTLE_NAME 记录",
S2.Reports.size == 1 &&
(S2.Reports[0] as ReportEntry).kind == ReportKind.BATTLE_NAME &&
(S2.Reports[0] as ReportEntry).targetId == "b1" &&
(S2.Reports[0] as ReportEntry).expireTime > 0.0
)
val S3 = ReduceUserReports(
ReduceUserReports(S2, ReportLobbyChatMessage("m1")),
ReportBattleChatMessage("m2"),
)
Check(
"切片 UserReports:两种聊天举报各追加一条",
S3.Reports.size == 3 &&
(S3.Reports[1] as ReportEntry).kind == ReportKind.LOBBY_CHAT_MESSAGE &&
(S3.Reports[2] as ReportEntry).kind == ReportKind.BATTLE_CHAT_MESSAGE
)
Check(
"切片 UserReports:无关动作不改状态",
ReduceUserReports(S3, AddFrags(1)) == S3
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ReportProBattleName("b"))
Check(
"根:ReportProBattleName 只动 userReportsState",
Root1.UserReportsState.Reports.size == 1 &&
Root1.Register == Root0.Register
)
val W = RootWiring.ByField("userReportsState")!!
Check(
"根:userReportsState 委派到 Zst / qot.zth 且 6 个动作",
W.SliceClass == "Zst" && W.Reducer == "qot.zth" && W.Actions.size == 6
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)