feat(state): 批量迁移 accountSettings/proBattle/language/announcement 切片
This commit is contained in:
1 parent
6947fcb567
commit
006e6a7ebd
17 files changed
+749
-53
No files matched your search
@@ -143,7 +143,7 @@
|
|||||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||||
- 已迁移切片 **14/89**:
|
- 已迁移切片 **18/89**:
|
||||||
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
||||||
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
||||||
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
||||||
@@ -197,12 +197,25 @@
|
|||||||
- `settings` → `Uut` / `xw.ltg`(`SettingsReducer.kt`):2 个字段,2 个 `instanceof` 臂。
|
- `settings` → `Uut` / `xw.ltg`(`SettingsReducer.kt`):2 个字段,2 个 `instanceof` 臂。
|
||||||
`currentCategory` 是设置页分类枚举(`ACCOUNT`…`SUPPORT`,默认 `GAME`),
|
`currentCategory` 是设置页分类枚举(`ACCOUNT`…`SUPPORT`,默认 `GAME`),
|
||||||
`disableAccountSettings` 默认 false;`Fut`→`SetSettingsCategory`、`Gut`→`DisableAccountSettings`。
|
`disableAccountSettings` 默认 false;`Fut`→`SetSettingsCategory`、`Gut`→`DisableAccountSettings`。
|
||||||
|
- `accountSettings` → `oet` / `Lp.mtg`(`AccountSettingsReducer.kt`):13 字段,13 个 `instanceof` 臂。
|
||||||
|
`newPasswordStatus` 用 `PasswordStatus`(`NONE/OK/SIMPLE/NOT_EQUALS`);
|
||||||
|
`ResetPasswordFields` 只清三个密码输入、状态和解锁按钮;`removalTimer` 存时间戳 `Long?`;
|
||||||
|
`locationCheckEnabled` 没有写入臂;11 个匿名动作按效果定名并登记。
|
||||||
|
- `proBattle` → `Wtt` / `uv.wth`(`ProBattleReducer.kt`):3 字段,2 个 `instanceof` 臂。
|
||||||
|
默认 `lastFightBattleId=-1`、`lastFightBattleTeam=NONE`、`battlePasswords=emptyMap()`;
|
||||||
|
`Fight` 写 id/team,`SetPassword` 空串删键、非空覆盖。登录切片的 `SetPassword` 是不同类,
|
||||||
|
这里用 `App.State.Reducers.SetPassword(battleId, password)`,避免丢 `battleId`。
|
||||||
|
- `language` → 枚举值 / `m6().jtg`(`LanguageReducer.kt`):`language` 在根状态里是枚举对象本身,
|
||||||
|
默认 `EN`;`d6`→`SetLanguage` 只写回语言,不包 StateSlice。
|
||||||
|
- `announcement` → `lC` / `PC().rq5`(`AnnouncementReducer.kt`):5 字段,4 个 `instanceof` 臂。
|
||||||
|
`isEnabled` 缺省 true,其余 false/空列表;`bC`→`DisableAnnouncement`、`qC`→`SetAnnouncementModelLoaded`、
|
||||||
|
`CC`→`SetAnnouncementItems`(生成物 `Init` 属于另一个切片,已改名)、`$C`→`SetSkipNextPromotedAnnounce`。
|
||||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 13 个有 reducer 的切片,
|
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 17 个有 reducer 的切片,
|
||||||
`launchParams` 与其余 **75 个字段原样透传**;
|
`launchParams` 与其余 **71 个字段原样透传**;
|
||||||
迁移进度用 `RootReducer.Coverage`(`已迁移 14/89`)与 `RootWiring.Unmigrated` 暴露。
|
迁移进度用 `RootReducer.Coverage`(`已迁移 18/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **325 项全绿**,含 89 槽位完整性、13 个归约切片一次归约互不串片、未迁移字段透传,
|
- 自检:`TankiOffline/tools/RunStateTest.ps1` **350 项全绿**,含 89 槽位完整性、17 个归约切片一次归约互不串片、未迁移字段透传,
|
||||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||||
@@ -212,7 +225,8 @@
|
|||||||
效果是把 `HomeScreen.shopDiscountNotification` 置 true,对应主菜单商店项的折扣提示);
|
效果是把 `HomeScreen.shopDiscountNotification` 置 true,对应主菜单商店项的折扣提示);
|
||||||
shop 的 17 个匿名类按 reducer 效果定名并登记(`Yut`→`ShopLoaded`、`F5`→`ResetShop` 等);
|
shop 的 17 个匿名类按 reducer 效果定名并登记(`Yut`→`ShopLoaded`、`F5`→`ResetShop` 等);
|
||||||
screen 的 9 个、lootBoxes 的 6 个、garage 的 25 个、garagePreview 的 15 个、
|
screen 的 9 个、lootBoxes 的 6 个、garage 的 25 个、garagePreview 的 15 个、
|
||||||
entrance 的 6 个、user 的 3 个、settings 的 2 个匿名类同样按效果定名并登记。
|
entrance 的 6 个、user 的 3 个、settings 的 2 个、accountSettings 的 11 个、
|
||||||
|
language 的 1 个、announcement 的 3 个匿名类同样按效果定名并登记。
|
||||||
|
|
||||||
### S4 引擎核心
|
### S4 引擎核心
|
||||||
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
||||||
|
|||||||
+115
-21
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generatedAt": "2026-10-23T09:11:41.133Z",
|
"generatedAt": "2026-10-23T09:52:15.365Z",
|
||||||
"module": "module_10032.js",
|
"module": "module_10032.js",
|
||||||
"RootState": {
|
"RootState": {
|
||||||
"Class": "z6",
|
"Class": "z6",
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
"entrance",
|
"entrance",
|
||||||
"user",
|
"user",
|
||||||
"settings",
|
"settings",
|
||||||
|
"accountSettings",
|
||||||
|
"proBattle",
|
||||||
|
"language",
|
||||||
|
"announcement",
|
||||||
"launchParams"
|
"launchParams"
|
||||||
],
|
],
|
||||||
"AnonymousActions": [
|
"AnonymousActions": [
|
||||||
@@ -527,6 +531,96 @@
|
|||||||
"Name": "DisableAccountSettings",
|
"Name": "DisableAccountSettings",
|
||||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
"Effect": "disableAccountSettings = true(`n.bu8(cd, !0)`)"
|
"Effect": "disableAccountSettings = true(`n.bu8(cd, !0)`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "aet",
|
||||||
|
"Name": "ResetPasswordFields",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "currentPassword/newPassword/repeatNewPassword = \"\",newPasswordStatus = NONE,isUnlockButtonForRenameUid = false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "fet",
|
||||||
|
"Name": "SetCurrentPassword",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "currentPassword = action.currentPassword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "vet",
|
||||||
|
"Name": "SetNewPassword",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "newPassword = action.newPassword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "pet",
|
||||||
|
"Name": "SetRepeatNewPassword",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "repeatNewPassword = action.repeatNewPassword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "wet",
|
||||||
|
"Name": "SetPasswordStatus",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "newPasswordStatus = action.status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "det",
|
||||||
|
"Name": "SetCurrentPasswordMandatory",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isCurrentPasswordMandatory = action.isMandatory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "get",
|
||||||
|
"Name": "SetEmail",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "email = action.email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "met",
|
||||||
|
"Name": "SetEmailConfirmed",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "emailConfirmed = action.emailConfirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "cet",
|
||||||
|
"Name": "SetCountry",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "countryCode / countryName = action 的两个字段"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "ket",
|
||||||
|
"Name": "SetUnlockButtonForRenameUid",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isUnlockButtonForRenameUid = action.isUnlockButtonForRenameUid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "xet",
|
||||||
|
"Name": "ClearAccountRemovalTimer",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "removalTimer = null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "d6",
|
||||||
|
"Name": "SetLanguage",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "language = action.language(`t instanceof d6 ? t.itg_1 : n`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "bC",
|
||||||
|
"Name": "DisableAnnouncement",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isEnabled = false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "qC",
|
||||||
|
"Name": "SetAnnouncementModelLoaded",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "modelLoaded = true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "CC",
|
||||||
|
"Name": "SetAnnouncementItems",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "items = action.items"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Markers": [
|
"Markers": [
|
||||||
@@ -1131,22 +1225,22 @@
|
|||||||
"Reducer": "Lp.mtg",
|
"Reducer": "Lp.mtg",
|
||||||
"Delegate": "Lp.mtg(t, n.vpv_1)",
|
"Delegate": "Lp.mtg(t, n.vpv_1)",
|
||||||
"Actions": [
|
"Actions": [
|
||||||
"?aet",
|
"ClearAccountRemovalTimer",
|
||||||
"?cet",
|
|
||||||
"?det",
|
|
||||||
"?fet",
|
|
||||||
"?get",
|
|
||||||
"?ket",
|
|
||||||
"?met",
|
|
||||||
"?pet",
|
|
||||||
"?vet",
|
|
||||||
"?wet",
|
|
||||||
"?xet",
|
|
||||||
"ClearInputUid",
|
"ClearInputUid",
|
||||||
"SetAccountRemovalDays"
|
"ResetPasswordFields",
|
||||||
|
"SetAccountRemovalDays",
|
||||||
|
"SetCountry",
|
||||||
|
"SetCurrentPassword",
|
||||||
|
"SetCurrentPasswordMandatory",
|
||||||
|
"SetEmail",
|
||||||
|
"SetEmailConfirmed",
|
||||||
|
"SetNewPassword",
|
||||||
|
"SetPasswordStatus",
|
||||||
|
"SetRepeatNewPassword",
|
||||||
|
"SetUnlockButtonForRenameUid"
|
||||||
],
|
],
|
||||||
"Default": "new oet()",
|
"Default": "new oet()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "lootBoxes",
|
"Field": "lootBoxes",
|
||||||
@@ -1838,10 +1932,10 @@
|
|||||||
"Reducer": "m6().jtg",
|
"Reducer": "m6().jtg",
|
||||||
"Delegate": "m6().jtg(t, n.gpx_1)",
|
"Delegate": "m6().jtg(t, n.gpx_1)",
|
||||||
"Actions": [
|
"Actions": [
|
||||||
"?d6"
|
"SetLanguage"
|
||||||
],
|
],
|
||||||
"Default": "tj()",
|
"Default": "tj()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "launchParams",
|
"Field": "launchParams",
|
||||||
@@ -1969,13 +2063,13 @@
|
|||||||
"Reducer": "PC().rq5",
|
"Reducer": "PC().rq5",
|
||||||
"Delegate": "PC().rq5(t, n.opx_1)",
|
"Delegate": "PC().rq5(t, n.opx_1)",
|
||||||
"Actions": [
|
"Actions": [
|
||||||
"?bC",
|
"DisableAnnouncement",
|
||||||
"?qC",
|
"SetAnnouncementItems",
|
||||||
"Init",
|
"SetAnnouncementModelLoaded",
|
||||||
"SetSkipNextPromotedAnnounce"
|
"SetSkipNextPromotedAnnounce"
|
||||||
],
|
],
|
||||||
"Default": "new lC()",
|
"Default": "new lC()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "notificationByCountry",
|
"Field": "notificationByCountry",
|
||||||
@@ -2165,7 +2259,7 @@
|
|||||||
"SetPassword"
|
"SetPassword"
|
||||||
],
|
],
|
||||||
"Default": "new Wtt()",
|
"Default": "new Wtt()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "availabilityByRank",
|
"Field": "availabilityByRank",
|
||||||
|
|||||||
@@ -569,6 +569,10 @@ function InferDeep(Expr, Mod, Off, Depth) {
|
|||||||
// 类型定义见 TankiOffline/.../App/State/ScreenId.kt。
|
// 类型定义见 TankiOffline/.../App/State/ScreenId.kt。
|
||||||
const FieldTypeOverrides = new Map([
|
const FieldTypeOverrides = new Map([
|
||||||
['GoTo.screen', 'ScreenId'],
|
['GoTo.screen', 'ScreenId'],
|
||||||
|
// proBattle 切片:Fight 的字段在别处调用点只能推成 Any?,reducer 需要真实类型。
|
||||||
|
// battleId → Long,battleTeam → BattleTeam(module_31908 的 TEAM_A/TEAM_B/NONE)。
|
||||||
|
['Fight.battleId', 'Long'],
|
||||||
|
['Fight.battleTeam', 'BattleTeam'],
|
||||||
// lootBoxes 切片:lH/qH/yH 的字段在别处的调用点只能给出 Boolean / Any?,但 reducer 里分别是
|
// lootBoxes 切片:lH/qH/yH 的字段在别处的调用点只能给出 Boolean / Any?,但 reducer 里分别是
|
||||||
// ContainerType(默认 Ly()=STANDARD)、可空 ContainerType、及补给箱奖励列表。
|
// ContainerType(默认 Ly()=STANDARD)、可空 ContainerType、及补给箱奖励列表。
|
||||||
['SetCurrentContainer.type', 'ContainerType'],
|
['SetCurrentContainer.type', 'ContainerType'],
|
||||||
|
|||||||
@@ -136,6 +136,28 @@ const AnonymousActionNames = new Map([
|
|||||||
// 括号里是原 reducer 的效果;实现见 SettingsReducer.kt。
|
// 括号里是原 reducer 的效果;实现见 SettingsReducer.kt。
|
||||||
['Fut', 'SetSettingsCategory'],
|
['Fut', 'SetSettingsCategory'],
|
||||||
['Gut', 'DisableAccountSettings'],
|
['Gut', 'DisableAccountSettings'],
|
||||||
|
// accountSettings 切片(oet / Lp.mtg):11 个没有 toString / 不在 action_inventory 的类。
|
||||||
|
// 括号里是原 reducer 的效果;实现见 AccountSettingsReducer.kt。
|
||||||
|
['aet', 'ResetPasswordFields'],
|
||||||
|
['fet', 'SetCurrentPassword'],
|
||||||
|
['vet', 'SetNewPassword'],
|
||||||
|
['pet', 'SetRepeatNewPassword'],
|
||||||
|
['wet', 'SetPasswordStatus'],
|
||||||
|
['det', 'SetCurrentPasswordMandatory'],
|
||||||
|
['get', 'SetEmail'],
|
||||||
|
['met', 'SetEmailConfirmed'],
|
||||||
|
['cet', 'SetCountry'],
|
||||||
|
['ket', 'SetUnlockButtonForRenameUid'],
|
||||||
|
['xet', 'ClearAccountRemovalTimer'],
|
||||||
|
// language 切片(m6().jtg):1 个没有 toString / 不在 action_inventory 的类。
|
||||||
|
// 实现见 LanguageReducer.kt。
|
||||||
|
['d6', 'SetLanguage'],
|
||||||
|
// announcement 切片(lC / PC().rq5):3 个没有 toString / 不在 action_inventory 的类。
|
||||||
|
// CC 在 action_inventory 里叫 Init,但生成物 App.State.Init 是另一个切片的同名类,这里改名。
|
||||||
|
// 实现见 AnnouncementReducer.kt。
|
||||||
|
['bC', 'DisableAnnouncement'],
|
||||||
|
['qC', 'SetAnnouncementModelLoaded'],
|
||||||
|
['CC', 'SetAnnouncementItems'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||||
@@ -149,7 +171,9 @@ const NonActionInstanceof = new Map([
|
|||||||
|
|
||||||
/** 动作局部类名 → 动作名;未收录的返回 null。 */
|
/** 动作局部类名 → 动作名;未收录的返回 null。 */
|
||||||
function ActionNameOf(Local) {
|
function ActionNameOf(Local) {
|
||||||
return LocalToName.get(Local) || AnonymousActionNames.get(Local) || null;
|
// 匿名表优先:个别同名类(如 announcement 的 CC=Init)会用生成物里的名字,
|
||||||
|
// 但该生成物属于另一个切片,必须在匿名表里显式改名。
|
||||||
|
return AnonymousActionNames.get(Local) || LocalToName.get(Local) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- 源码扫描基础
|
// ---------------------------------------------------------------- 源码扫描基础
|
||||||
@@ -509,6 +533,16 @@ const Migrated = {
|
|||||||
entrance: { Type: 'EntranceStateSlice', Reduce: 'ReduceEntrance' },
|
entrance: { Type: 'EntranceStateSlice', Reduce: 'ReduceEntrance' },
|
||||||
user: { Type: 'UserStateSlice', Reduce: 'ReduceUser' },
|
user: { Type: 'UserStateSlice', Reduce: 'ReduceUser' },
|
||||||
settings: { Type: 'SettingsStateSlice', Reduce: 'ReduceSettings' },
|
settings: { Type: 'SettingsStateSlice', Reduce: 'ReduceSettings' },
|
||||||
|
accountSettings: { Type: 'AccountSettingsStateSlice', Reduce: 'ReduceAccountSettings' },
|
||||||
|
proBattle: { Type: 'ProBattleStateSlice', Reduce: 'ReduceProBattle' },
|
||||||
|
// language 在原根状态里是枚举对象本身,不是对象切片;Default/Import 显式指定。
|
||||||
|
language: {
|
||||||
|
Type: 'Language',
|
||||||
|
Default: 'App.State.Language.EN',
|
||||||
|
Reduce: 'ReduceLanguage',
|
||||||
|
Import: false,
|
||||||
|
},
|
||||||
|
announcement: { Type: 'AnnouncementStateSlice', Reduce: 'ReduceAnnouncement' },
|
||||||
// 原根 reducer 对这一片没有调用,只有默认值 `ag()`(空 Map);这里仅做类型收口。
|
// 原根 reducer 对这一片没有调用,只有默认值 `ag()`(空 Map);这里仅做类型收口。
|
||||||
launchParams: {
|
launchParams: {
|
||||||
Type: 'LaunchParamsStateSlice',
|
Type: 'LaunchParamsStateSlice',
|
||||||
@@ -651,6 +685,33 @@ const SettingsAnonymousEffects = {
|
|||||||
Gut: 'disableAccountSettings = true(`n.bu8(cd, !0)`)',
|
Gut: 'disableAccountSettings = true(`n.bu8(cd, !0)`)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// accountSettings 切片(oet / Lp.mtg):没有 toString / 不在 action_inventory 的类,按 reducer 分支效果定名。
|
||||||
|
const AccountSettingsAnonymousEffects = {
|
||||||
|
aet: 'currentPassword/newPassword/repeatNewPassword = "",newPasswordStatus = NONE,isUnlockButtonForRenameUid = false',
|
||||||
|
fet: 'currentPassword = action.currentPassword',
|
||||||
|
vet: 'newPassword = action.newPassword',
|
||||||
|
pet: 'repeatNewPassword = action.repeatNewPassword',
|
||||||
|
wet: 'newPasswordStatus = action.status',
|
||||||
|
det: 'isCurrentPasswordMandatory = action.isMandatory',
|
||||||
|
get: 'email = action.email',
|
||||||
|
met: 'emailConfirmed = action.emailConfirmed',
|
||||||
|
cet: 'countryCode / countryName = action 的两个字段',
|
||||||
|
ket: 'isUnlockButtonForRenameUid = action.isUnlockButtonForRenameUid',
|
||||||
|
xet: 'removalTimer = null',
|
||||||
|
};
|
||||||
|
|
||||||
|
// language 切片(m6().jtg):只把动作里的语言枚举写回。
|
||||||
|
const LanguageAnonymousEffects = {
|
||||||
|
d6: 'language = action.language(`t instanceof d6 ? t.itg_1 : n`)',
|
||||||
|
};
|
||||||
|
|
||||||
|
// announcement 切片(lC / PC().rq5):没有 toString / 不在 action_inventory 的类,按 reducer 分支效果定名。
|
||||||
|
const AnnouncementAnonymousEffects = {
|
||||||
|
bC: 'isEnabled = false',
|
||||||
|
qC: 'modelLoaded = true',
|
||||||
|
CC: 'items = action.items',
|
||||||
|
};
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(P, 'names', 'root_wiring.json'),
|
path.join(P, 'names', 'root_wiring.json'),
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
@@ -715,6 +776,24 @@ fs.writeFileSync(
|
|||||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
Effect,
|
Effect,
|
||||||
})),
|
})),
|
||||||
|
...Object.entries(AccountSettingsAnonymousEffects).map(([Local, Effect]) => ({
|
||||||
|
Local,
|
||||||
|
Name: AnonymousActionNames.get(Local),
|
||||||
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
|
Effect,
|
||||||
|
})),
|
||||||
|
...Object.entries(LanguageAnonymousEffects).map(([Local, Effect]) => ({
|
||||||
|
Local,
|
||||||
|
Name: AnonymousActionNames.get(Local),
|
||||||
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
|
Effect,
|
||||||
|
})),
|
||||||
|
...Object.entries(AnnouncementAnonymousEffects).map(([Local, Effect]) => ({
|
||||||
|
Local,
|
||||||
|
Name: AnonymousActionNames.get(Local),
|
||||||
|
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||||
|
Effect,
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
Markers: [
|
Markers: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// AccountSettingsModels.kt —— accountSettings 切片用到的枚举(App.State 包内)
|
||||||
|
//
|
||||||
|
// 证据链:module_10032 的 `_et()` 初始化
|
||||||
|
// `NONE=0, OK=1, SIMPLE=2, NOT_EQUALS=3`;`Ret()` 返回 NONE,
|
||||||
|
// 即 `oet.newPasswordStatus` 的默认值。
|
||||||
|
package App.State
|
||||||
|
|
||||||
|
/** 原 `het`:新密码校验状态,顺序与枚举 ordinal 一致。 */
|
||||||
|
enum class PasswordStatus {
|
||||||
|
NONE,
|
||||||
|
OK,
|
||||||
|
SIMPLE,
|
||||||
|
NOT_EQUALS,
|
||||||
|
}
|
||||||
@@ -721,7 +721,7 @@ object ActionCatalog {
|
|||||||
ActionMeta("SelectBattle", SelectBattle::class, listOf(ActionFieldMeta("battleId", "Long"))),
|
ActionMeta("SelectBattle", SelectBattle::class, listOf(ActionFieldMeta("battleId", "Long"))),
|
||||||
ActionMeta("SelectBattleFormat", SelectBattleFormat::class, listOf(ActionFieldMeta("format", "BattleFormat"))),
|
ActionMeta("SelectBattleFormat", SelectBattleFormat::class, listOf(ActionFieldMeta("format", "BattleFormat"))),
|
||||||
ActionMeta("ProBattleState", ProBattleState::class, listOf(ActionFieldMeta("lastFightBattleId", "Any?"), ActionFieldMeta("lastFightBattleTeam", "Any?"), ActionFieldMeta("battlePasswords", "Any?"))),
|
ActionMeta("ProBattleState", ProBattleState::class, listOf(ActionFieldMeta("lastFightBattleId", "Any?"), ActionFieldMeta("lastFightBattleTeam", "Any?"), ActionFieldMeta("battlePasswords", "Any?"))),
|
||||||
ActionMeta("Fight", Fight::class, listOf(ActionFieldMeta("battleId", "Any?"), ActionFieldMeta("battleTeam", "Any?"))),
|
ActionMeta("Fight", Fight::class, listOf(ActionFieldMeta("battleId", "Long"), ActionFieldMeta("battleTeam", "BattleTeam"))),
|
||||||
ActionMeta("Spectate", Spectate::class, listOf(ActionFieldMeta("battleId", "Any?"))),
|
ActionMeta("Spectate", Spectate::class, listOf(ActionFieldMeta("battleId", "Any?"))),
|
||||||
ActionMeta("CheckPassword", CheckPassword::class, listOf(ActionFieldMeta("battleId", "Any?"), ActionFieldMeta("password", "Any?"))),
|
ActionMeta("CheckPassword", CheckPassword::class, listOf(ActionFieldMeta("battleId", "Any?"), ActionFieldMeta("password", "Any?"))),
|
||||||
ActionMeta("GoToBattleInfo", GoToBattleInfo::class, listOf(ActionFieldMeta("battleId", "Any?"))),
|
ActionMeta("GoToBattleInfo", GoToBattleInfo::class, listOf(ActionFieldMeta("battleId", "Any?"))),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package App.State
|
|||||||
|
|
||||||
import Core.Vector3
|
import Core.Vector3
|
||||||
|
|
||||||
// 动作/状态类共 816 个,字段 2150 个(已定类型 584 个)
|
// 动作/状态类共 816 个,字段 2150 个(已定类型 586 个)
|
||||||
data class AnnouncementState(
|
data class AnnouncementState(
|
||||||
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
|
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
|
||||||
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
|
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
|
||||||
@@ -4077,8 +4077,8 @@ data class ProBattleState(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class Fight(
|
data class Fight(
|
||||||
val battleId: Any?, // 调用点: P2t().kar().paq_1.bpy_1.fto_1 | t.props.battleId
|
val battleId: Long, // override
|
||||||
val battleTeam: Any?, // 调用点: P2t().kar().paq_1.bpy_1.gto_1 | t.props.battleTeam
|
val battleTeam: BattleTeam, // override
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Spectate(
|
data class Spectate(
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// LanguageModels.kt —— language 切片的值类型(App.State 包内)
|
||||||
|
//
|
||||||
|
// 证据链:module_52943 的 `lo()` 初始化
|
||||||
|
// `EN=0, RU=1, DE=2, ES=3, PL=4, PT=5, ZH=6, FR=7, JA=8, TR=9, CS=10, HI=11`;
|
||||||
|
// `tj()` 返回 `lt`,即根状态 language 的默认值 EN。
|
||||||
|
// language 在根状态里是枚举对象本身,不是对象切片,因此不额外包一层 StateSlice。
|
||||||
|
package App.State
|
||||||
|
|
||||||
|
/** 原 `fo`:客户端语言,顺序与枚举 ordinal 一致。 */
|
||||||
|
enum class Language {
|
||||||
|
EN,
|
||||||
|
RU,
|
||||||
|
DE,
|
||||||
|
ES,
|
||||||
|
PL,
|
||||||
|
PT,
|
||||||
|
ZH,
|
||||||
|
FR,
|
||||||
|
JA,
|
||||||
|
TR,
|
||||||
|
CS,
|
||||||
|
HI,
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// ProBattleModels.kt —— proBattle 切片用到的枚举(App.State 包内)
|
||||||
|
//
|
||||||
|
// 证据链:module_31908 的 `Ir()` 初始化
|
||||||
|
// `TEAM_A=0, TEAM_B=1, NONE=2`;module_10032 的 `em()` 返回 NONE,
|
||||||
|
// 即 `Wtt.lastFightBattleTeam` 的默认值。
|
||||||
|
package App.State
|
||||||
|
|
||||||
|
/** 原 `Ar`:上一场战斗的队伍,顺序与枚举 ordinal 一致。 */
|
||||||
|
enum class BattleTeam {
|
||||||
|
TEAM_A,
|
||||||
|
TEAM_B,
|
||||||
|
NONE,
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// AccountSettingsReducer.kt —— 账户设置切片(对应 module_10032 的 oet 状态 + Lp.mtg reducer)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 状态类 oet:13 个字段,toString 名 "AccountSettings(...)";copy 是 nu6(13 字段 + 回调)。
|
||||||
|
// 默认:isCurrentPasswordMandatory=false、三个密码字段空串、newPasswordStatus=NONE、
|
||||||
|
// countryCode/countryName/email=null、emailConfirmed/locationCheckEnabled=false、
|
||||||
|
// isUnlockButtonForRenameUid/isClearInputUid=false、removalTimer=null。
|
||||||
|
// 根委派:`Lp.mtg(t, n.vpv_1)`。
|
||||||
|
// 13 个 instanceof 臂见下方 reducer;`locationCheckEnabled` 没有写入臂。
|
||||||
|
// `removalTimer` 原值由 `cm(t.au7_1)` 生成 Kotlin Date,这里存时间戳 Long?,语义仍是同一时刻。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.ClearInputUid
|
||||||
|
import App.State.PasswordStatus
|
||||||
|
import App.State.SetAccountRemovalDays
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 匿名动作 */
|
||||||
|
|
||||||
|
/** 原 `aet`:清空三个密码输入,把校验状态打回 NONE,并复位改名解锁按钮。 */
|
||||||
|
object ResetPasswordFields
|
||||||
|
|
||||||
|
class SetCurrentPassword(val currentPassword: String)
|
||||||
|
|
||||||
|
class SetNewPassword(val newPassword: String)
|
||||||
|
|
||||||
|
class SetRepeatNewPassword(val repeatNewPassword: String)
|
||||||
|
|
||||||
|
class SetPasswordStatus(val status: PasswordStatus)
|
||||||
|
|
||||||
|
class SetCurrentPasswordMandatory(val isMandatory: Boolean)
|
||||||
|
|
||||||
|
class SetEmail(val email: String?)
|
||||||
|
|
||||||
|
class SetEmailConfirmed(val emailConfirmed: Boolean)
|
||||||
|
|
||||||
|
class SetCountry(val countryCode: String?, val countryName: String?)
|
||||||
|
|
||||||
|
class SetUnlockButtonForRenameUid(val isUnlockButtonForRenameUid: Boolean)
|
||||||
|
|
||||||
|
/** 原 `xet`:只把 removalTimer 置 null。 */
|
||||||
|
object ClearAccountRemovalTimer
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 状态 */
|
||||||
|
|
||||||
|
data class AccountSettingsStateSlice(
|
||||||
|
val IsCurrentPasswordMandatory: Boolean = false,
|
||||||
|
val CurrentPassword: String = "",
|
||||||
|
val NewPassword: String = "",
|
||||||
|
val RepeatNewPassword: String = "",
|
||||||
|
val NewPasswordStatus: PasswordStatus = PasswordStatus.NONE,
|
||||||
|
val CountryCode: String? = null,
|
||||||
|
val CountryName: String? = null,
|
||||||
|
val Email: String? = null,
|
||||||
|
val EmailConfirmed: Boolean = false,
|
||||||
|
val LocationCheckEnabled: Boolean = false,
|
||||||
|
val IsUnlockButtonForRenameUid: Boolean = false,
|
||||||
|
val IsClearInputUid: Boolean = false,
|
||||||
|
val RemovalTimer: Long? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ reducer */
|
||||||
|
|
||||||
|
fun ReduceAccountSettings(
|
||||||
|
State0: AccountSettingsStateSlice,
|
||||||
|
Action: Any,
|
||||||
|
): AccountSettingsStateSlice = when (Action) {
|
||||||
|
is ResetPasswordFields -> State0.copy(
|
||||||
|
CurrentPassword = "",
|
||||||
|
NewPassword = "",
|
||||||
|
RepeatNewPassword = "",
|
||||||
|
NewPasswordStatus = PasswordStatus.NONE,
|
||||||
|
IsUnlockButtonForRenameUid = false,
|
||||||
|
)
|
||||||
|
is SetCurrentPassword -> State0.copy(CurrentPassword = Action.currentPassword)
|
||||||
|
is SetNewPassword -> State0.copy(NewPassword = Action.newPassword)
|
||||||
|
is SetRepeatNewPassword -> State0.copy(RepeatNewPassword = Action.repeatNewPassword)
|
||||||
|
is SetPasswordStatus -> State0.copy(NewPasswordStatus = Action.status)
|
||||||
|
is SetCurrentPasswordMandatory -> State0.copy(IsCurrentPasswordMandatory = Action.isMandatory)
|
||||||
|
is SetEmail -> State0.copy(Email = Action.email)
|
||||||
|
is SetEmailConfirmed -> State0.copy(EmailConfirmed = Action.emailConfirmed)
|
||||||
|
is SetCountry -> State0.copy(CountryCode = Action.countryCode, CountryName = Action.countryName)
|
||||||
|
is SetUnlockButtonForRenameUid ->
|
||||||
|
State0.copy(IsUnlockButtonForRenameUid = Action.isUnlockButtonForRenameUid)
|
||||||
|
is ClearInputUid -> State0.copy(IsClearInputUid = Action.isClearInputUid)
|
||||||
|
is SetAccountRemovalDays -> State0.copy(RemovalTimer = Action.removalTimer)
|
||||||
|
is ClearAccountRemovalTimer -> State0.copy(RemovalTimer = null)
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// AnnouncementReducer.kt —— 公告切片(对应 module_10032 的 lC 状态 + RC.rq5)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 状态类 lC:5 个字段,toString 名 "AnnouncementState(...)";copy 是 cq0(5 字段 + 回调)。
|
||||||
|
// 默认:isEnabled=true、modelLoaded=false、items=emptyList()、wasInBattle=false、
|
||||||
|
// skipNextPromotedAnnounce=false。
|
||||||
|
// 根委派:`PC().rq5(t, n.opx_1)`。
|
||||||
|
// reducer 四个 instanceof 臂:
|
||||||
|
// bC → isEnabled=false
|
||||||
|
// qC → modelLoaded=true
|
||||||
|
// CC → items=action.announcementItems(生成物里的 Init 是另一个切片的同名类,故单独建类)
|
||||||
|
// $C → skipNextPromotedAnnounce=action.skipShowNextAnnounce
|
||||||
|
// `wasInBattle` 没有写入臂。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.SetSkipNextPromotedAnnounce
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 匿名动作 */
|
||||||
|
|
||||||
|
/** 原 `bC`:关闭公告。 */
|
||||||
|
object DisableAnnouncement
|
||||||
|
|
||||||
|
/** 原 `qC`:公告模型已加载。 */
|
||||||
|
object SetAnnouncementModelLoaded
|
||||||
|
|
||||||
|
/** 原 `CC`:写入公告列表。 */
|
||||||
|
class SetAnnouncementItems(val items: List<Any?>)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 状态 */
|
||||||
|
|
||||||
|
data class AnnouncementStateSlice(
|
||||||
|
val IsEnabled: Boolean = true,
|
||||||
|
val ModelLoaded: Boolean = false,
|
||||||
|
val Items: List<Any?> = emptyList(),
|
||||||
|
val WasInBattle: Boolean = false,
|
||||||
|
val SkipNextPromotedAnnounce: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ reducer */
|
||||||
|
|
||||||
|
fun ReduceAnnouncement(
|
||||||
|
State0: AnnouncementStateSlice,
|
||||||
|
Action: Any,
|
||||||
|
): AnnouncementStateSlice = when (Action) {
|
||||||
|
is DisableAnnouncement -> State0.copy(IsEnabled = false)
|
||||||
|
is SetAnnouncementModelLoaded -> State0.copy(ModelLoaded = true)
|
||||||
|
is SetAnnouncementItems -> State0.copy(Items = Action.items)
|
||||||
|
is SetSkipNextPromotedAnnounce ->
|
||||||
|
State0.copy(SkipNextPromotedAnnounce = Action.skipShowNextAnnounce)
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// LanguageReducer.kt —— 语言切片(对应 module_10032 的 language 值 + g6.jtg)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 根状态 language 的默认值是 `tj()` → module_52943 的 `vo()` → EN。
|
||||||
|
// reducer:`t instanceof d6 ? t.itg_1 : n`,即只把动作里的语言枚举写回。
|
||||||
|
// language 在原根状态里就是枚举对象本身,因此 reducer 直接 (Language, Any) → Language。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.Language
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 动作 */
|
||||||
|
|
||||||
|
class SetLanguage(val language: Language)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ reducer */
|
||||||
|
|
||||||
|
fun ReduceLanguage(State0: Language, Action: Any): Language = when (Action) {
|
||||||
|
is SetLanguage -> Action.language
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// ProBattleReducer.kt —— 上一场战斗 / 战斗密码切片(对应 module_10032 的 Wtt 状态 + Jtt.wth)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 状态类 Wtt:3 个字段,toString 名 "ProBattleState(...)";copy 是 jto(3 字段 + 回调)。
|
||||||
|
// 默认:lastFightBattleId=-1、lastFightBattleTeam=NONE、battlePasswords=emptyMap()。
|
||||||
|
// 根委派:`uv.wth(t, n.bpy_1)`。
|
||||||
|
// reducer:
|
||||||
|
// `t instanceof Vtt ? n.jto(t.lto_1, t.mto_1)`(Fight 只写 id + team)
|
||||||
|
// `t instanceof Xtt ? password 为空 ? n.jto(cd, cd, am(map, id)) : n.jto(cd, cd, cg(map, Vd(id, password)))`
|
||||||
|
// `SetPassword` 在登录切片也有同名类(qD,只有 password),这里按 proBattle 的 Xtt 建独立类,
|
||||||
|
// 避免复用生成物 App.State.SetPassword 导致丢 battleId。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.BattleTeam
|
||||||
|
import App.State.Fight
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 动作 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原 `Xtt`:i = battleId,password = 新密码。
|
||||||
|
* 密码为空时从 map 删除,否则覆盖写入。
|
||||||
|
*/
|
||||||
|
class SetPassword(val battleId: Long, val password: String)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 状态 */
|
||||||
|
|
||||||
|
data class ProBattleStateSlice(
|
||||||
|
val LastFightBattleId: Long = -1L,
|
||||||
|
val LastFightBattleTeam: BattleTeam = BattleTeam.NONE,
|
||||||
|
val BattlePasswords: Map<Long, String> = emptyMap(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ reducer */
|
||||||
|
|
||||||
|
fun ReduceProBattle(State0: ProBattleStateSlice, Action: Any): ProBattleStateSlice = when (Action) {
|
||||||
|
is Fight -> State0.copy(
|
||||||
|
LastFightBattleId = Action.battleId,
|
||||||
|
LastFightBattleTeam = Action.battleTeam,
|
||||||
|
)
|
||||||
|
is SetPassword -> State0.copy(
|
||||||
|
BattlePasswords = if (Action.password.isEmpty()) {
|
||||||
|
State0.BattlePasswords - Action.battleId
|
||||||
|
} else {
|
||||||
|
State0.BattlePasswords + (Action.battleId to Action.password)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||||
//
|
//
|
||||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||||
// 这里采用同一形状。已迁移的 14 个切片里,13 个真正调用 reducer,launchParams 只做类型收口:
|
// 这里采用同一形状。已迁移的 18 个切片里,17 个真正调用 reducer,launchParams 只做类型收口:
|
||||||
//
|
//
|
||||||
// inviteState → ReduceInvite(aD / pD.er5)
|
// inviteState → ReduceInvite(aD / pD.er5)
|
||||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||||
@@ -16,21 +16,29 @@
|
|||||||
// entrance → ReduceEntrance(hD / Bv.pth,根委派 `Bv.pth(t, n.jpx_1)`)
|
// entrance → ReduceEntrance(hD / Bv.pth,根委派 `Bv.pth(t, n.jpx_1)`)
|
||||||
// user → ReduceUser(t8 / M8().qtd,根委派 `M8().qtd(t, n.ypu_1)`)
|
// user → ReduceUser(t8 / M8().qtd,根委派 `M8().qtd(t, n.ypu_1)`)
|
||||||
// settings → ReduceSettings(Uut / xw.ltg,根委派 `xw.ltg(t, n.upv_1)`)
|
// settings → ReduceSettings(Uut / xw.ltg,根委派 `xw.ltg(t, n.upv_1)`)
|
||||||
|
// accountSettings → ReduceAccountSettings(oet / Lp.mtg,根委派 `Lp.mtg(t, n.vpv_1)`)
|
||||||
|
// proBattle → ReduceProBattle(Wtt / uv.wth,根委派 `uv.wth(t, n.bpy_1)`)
|
||||||
|
// language → ReduceLanguage(枚举值 / m6().jtg,根委派 `m6().jtg(t, n.gpx_1)`)
|
||||||
|
// announcement → ReduceAnnouncement(lC / PC().rq5,根委派 `PC().rq5(t, n.opx_1)`)
|
||||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||||
//
|
//
|
||||||
// 其余 75 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
// 其余 71 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||||
package App.State
|
package App.State
|
||||||
|
|
||||||
|
import App.State.Reducers.ReduceAccountSettings
|
||||||
|
import App.State.Reducers.ReduceAnnouncement
|
||||||
import App.State.Reducers.ReduceEntrance
|
import App.State.Reducers.ReduceEntrance
|
||||||
import App.State.Reducers.ReduceGarage
|
import App.State.Reducers.ReduceGarage
|
||||||
import App.State.Reducers.ReduceGaragePreview
|
import App.State.Reducers.ReduceGaragePreview
|
||||||
import App.State.Reducers.ReduceHome
|
import App.State.Reducers.ReduceHome
|
||||||
import App.State.Reducers.ReduceInvite
|
import App.State.Reducers.ReduceInvite
|
||||||
|
import App.State.Reducers.ReduceLanguage
|
||||||
import App.State.Reducers.ReduceLocalBattleUser
|
import App.State.Reducers.ReduceLocalBattleUser
|
||||||
import App.State.Reducers.ReduceLoginByPassword
|
import App.State.Reducers.ReduceLoginByPassword
|
||||||
import App.State.Reducers.ReduceLootBoxes
|
import App.State.Reducers.ReduceLootBoxes
|
||||||
|
import App.State.Reducers.ReduceProBattle
|
||||||
import App.State.Reducers.ReduceScreen
|
import App.State.Reducers.ReduceScreen
|
||||||
import App.State.Reducers.ReduceSettings
|
import App.State.Reducers.ReduceSettings
|
||||||
import App.State.Reducers.ReduceShop
|
import App.State.Reducers.ReduceShop
|
||||||
@@ -63,6 +71,10 @@ object RootReducer {
|
|||||||
Entrance = ReduceEntrance(State0.Entrance, Action),
|
Entrance = ReduceEntrance(State0.Entrance, Action),
|
||||||
User = ReduceUser(State0.User, Action),
|
User = ReduceUser(State0.User, Action),
|
||||||
Settings = ReduceSettings(State0.Settings, Action),
|
Settings = ReduceSettings(State0.Settings, Action),
|
||||||
|
AccountSettings = ReduceAccountSettings(State0.AccountSettings, Action),
|
||||||
|
ProBattle = ReduceProBattle(State0.ProBattle, Action),
|
||||||
|
Language = ReduceLanguage(State0.Language, Action),
|
||||||
|
Announcement = ReduceAnnouncement(State0.Announcement, Action),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
// 已迁移的切片用强类型;其余保持 Any?(原样透传),迁移一个改一个再重新生成。
|
// 已迁移的切片用强类型;其余保持 Any?(原样透传),迁移一个改一个再重新生成。
|
||||||
package App.State
|
package App.State
|
||||||
|
|
||||||
|
import App.State.Reducers.AccountSettingsStateSlice
|
||||||
|
import App.State.Reducers.AnnouncementStateSlice
|
||||||
import App.State.Reducers.EntranceStateSlice
|
import App.State.Reducers.EntranceStateSlice
|
||||||
import App.State.Reducers.GarageStateSlice
|
import App.State.Reducers.GarageStateSlice
|
||||||
import App.State.Reducers.GaragePreviewStateSlice
|
import App.State.Reducers.GaragePreviewStateSlice
|
||||||
@@ -12,6 +14,7 @@ import App.State.Reducers.InviteStateSlice
|
|||||||
import App.State.Reducers.LocalBattleUserStateSlice
|
import App.State.Reducers.LocalBattleUserStateSlice
|
||||||
import App.State.Reducers.LoginByPasswordStateSlice
|
import App.State.Reducers.LoginByPasswordStateSlice
|
||||||
import App.State.Reducers.LootBoxesStateSlice
|
import App.State.Reducers.LootBoxesStateSlice
|
||||||
|
import App.State.Reducers.ProBattleStateSlice
|
||||||
import App.State.Reducers.ScreenStateSlice
|
import App.State.Reducers.ScreenStateSlice
|
||||||
import App.State.Reducers.SettingsStateSlice
|
import App.State.Reducers.SettingsStateSlice
|
||||||
import App.State.Reducers.ShopStateSlice
|
import App.State.Reducers.ShopStateSlice
|
||||||
@@ -69,8 +72,8 @@ data class RootState(
|
|||||||
val News: Any? = null,
|
val News: Any? = null,
|
||||||
/** 原 settings(Uut / xw.ltg)。已迁移。 */
|
/** 原 settings(Uut / xw.ltg)。已迁移。 */
|
||||||
val Settings: SettingsStateSlice = SettingsStateSlice(),
|
val Settings: SettingsStateSlice = SettingsStateSlice(),
|
||||||
/** 原 accountSettings(oet / Lp.mtg)。未迁移:原样透传。 */
|
/** 原 accountSettings(oet / Lp.mtg)。已迁移。 */
|
||||||
val AccountSettings: Any? = null,
|
val AccountSettings: AccountSettingsStateSlice = AccountSettingsStateSlice(),
|
||||||
/** 原 lootBoxes(aH / ws.wrv)。已迁移。 */
|
/** 原 lootBoxes(aH / ws.wrv)。已迁移。 */
|
||||||
val LootBoxes: LootBoxesStateSlice = LootBoxesStateSlice(),
|
val LootBoxes: LootBoxesStateSlice = LootBoxesStateSlice(),
|
||||||
/** 原 notifications(? / B_.ise)。未迁移:原样透传。 */
|
/** 原 notifications(? / B_.ise)。未迁移:原样透传。 */
|
||||||
@@ -143,8 +146,8 @@ data class RootState(
|
|||||||
val TouchscreenHudSettings: Any? = null,
|
val TouchscreenHudSettings: Any? = null,
|
||||||
/** 原 mainQuest(WZ / ih.bsk)。未迁移:原样透传。 */
|
/** 原 mainQuest(WZ / ih.bsk)。未迁移:原样透传。 */
|
||||||
val MainQuest: Any? = null,
|
val MainQuest: Any? = null,
|
||||||
/** 原 language(? / m6().jtg)。未迁移:原样透传。 */
|
/** 原 language(? / m6().jtg)。已迁移。 */
|
||||||
val Language: Any? = null,
|
val Language: Language = App.State.Language.EN,
|
||||||
/** 原 launchParams(? / (pass-through))。已迁移(透传,强类型)。 */
|
/** 原 launchParams(? / (pass-through))。已迁移(透传,强类型)。 */
|
||||||
val LaunchParams: LaunchParamsStateSlice = emptyMap(),
|
val LaunchParams: LaunchParamsStateSlice = emptyMap(),
|
||||||
/** 原 partner(UJ / (inline@partner))。未迁移:原样透传。 */
|
/** 原 partner(UJ / (inline@partner))。未迁移:原样透传。 */
|
||||||
@@ -159,8 +162,8 @@ data class RootState(
|
|||||||
val Moderation: Any? = null,
|
val Moderation: Any? = null,
|
||||||
/** 原 rulesUpdated(i1 / Ch.dsm)。未迁移:原样透传。 */
|
/** 原 rulesUpdated(i1 / Ch.dsm)。未迁移:原样透传。 */
|
||||||
val RulesUpdated: Any? = null,
|
val RulesUpdated: Any? = null,
|
||||||
/** 原 announcement(lC / PC().rq5)。未迁移:原样透传。 */
|
/** 原 announcement(lC / PC().rq5)。已迁移。 */
|
||||||
val Announcement: Any? = null,
|
val Announcement: AnnouncementStateSlice = AnnouncementStateSlice(),
|
||||||
/** 原 notificationByCountry(gJ / R_.ssd)。未迁移:原样透传。 */
|
/** 原 notificationByCountry(gJ / R_.ssd)。未迁移:原样透传。 */
|
||||||
val NotificationByCountry: Any? = null,
|
val NotificationByCountry: Any? = null,
|
||||||
/** 原 userRenameState(O8 / Rf.pte)。未迁移:原样透传。 */
|
/** 原 userRenameState(O8 / Rf.pte)。未迁移:原样透传。 */
|
||||||
@@ -185,8 +188,8 @@ data class RootState(
|
|||||||
val SuppliesTutorial: Any? = null,
|
val SuppliesTutorial: Any? = null,
|
||||||
/** 原 questCategories(zZ / th.lsh)。未迁移:原样透传。 */
|
/** 原 questCategories(zZ / th.lsh)。未迁移:原样透传。 */
|
||||||
val QuestCategories: Any? = null,
|
val QuestCategories: Any? = null,
|
||||||
/** 原 proBattle(Wtt / uv.wth)。未迁移:原样透传。 */
|
/** 原 proBattle(Wtt / uv.wth)。已迁移。 */
|
||||||
val ProBattle: Any? = null,
|
val ProBattle: ProBattleStateSlice = ProBattleStateSlice(),
|
||||||
/** 原 availabilityByRank(tet / $p.xth)。未迁移:原样透传。 */
|
/** 原 availabilityByRank(tet / $p.xth)。未迁移:原样透传。 */
|
||||||
val AvailabilityByRank: Any? = null,
|
val AvailabilityByRank: Any? = null,
|
||||||
/** 原 userReportsState(Zst / qot.zth)。未迁移:原样透传。 */
|
/** 原 userReportsState(Zst / qot.zth)。未迁移:原样透传。 */
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ object RootWiring {
|
|||||||
SliceWiring("tipsState", "spv_1", "dst", "Hw.vth", "Hw.vth(t, n.spv_1)", listOf("UpdateTips")),
|
SliceWiring("tipsState", "spv_1", "dst", "Hw.vth", "Hw.vth(t, n.spv_1)", listOf("UpdateTips")),
|
||||||
SliceWiring("news", "tpv_1", "ZX", "E_.asd", "E_.asd(t, n.tpv_1)", listOf("?hJ", "AddNewsItemAction", "RemoveNewsItemAction", "SetNewsItemAction", "SetUnreadNewsCounter")),
|
SliceWiring("news", "tpv_1", "ZX", "E_.asd", "E_.asd(t, n.tpv_1)", listOf("?hJ", "AddNewsItemAction", "RemoveNewsItemAction", "SetNewsItemAction", "SetUnreadNewsCounter")),
|
||||||
SliceWiring("settings", "upv_1", "Uut", "xw.ltg", "xw.ltg(t, n.upv_1)", listOf("DisableAccountSettings", "SetSettingsCategory")),
|
SliceWiring("settings", "upv_1", "Uut", "xw.ltg", "xw.ltg(t, n.upv_1)", listOf("DisableAccountSettings", "SetSettingsCategory")),
|
||||||
SliceWiring("accountSettings", "vpv_1", "oet", "Lp.mtg", "Lp.mtg(t, n.vpv_1)", listOf("?aet", "?cet", "?det", "?fet", "?get", "?ket", "?met", "?pet", "?vet", "?wet", "?xet", "ClearInputUid", "SetAccountRemovalDays")),
|
SliceWiring("accountSettings", "vpv_1", "oet", "Lp.mtg", "Lp.mtg(t, n.vpv_1)", listOf("ClearAccountRemovalTimer", "ClearInputUid", "ResetPasswordFields", "SetAccountRemovalDays", "SetCountry", "SetCurrentPassword", "SetCurrentPasswordMandatory", "SetEmail", "SetEmailConfirmed", "SetNewPassword", "SetPasswordStatus", "SetRepeatNewPassword", "SetUnlockButtonForRenameUid")),
|
||||||
SliceWiring("lootBoxes", "wpv_1", "aH", "ws.wrv", "ws.wrv(t, n.wpv_1)", listOf("AddKeys", "ClearRewards", "LootBoxOpeningFailed", "SelectCategory", "SetContainerData", "SetCurrentContainer", "SetGoToGarage", "SetIsAccelerated", "SetIsBlockOpenButtons", "SetIsOpening", "SetIsReopening", "SetIsShopVisited", "SetKeys", "SetPeriodOfGiveLootBox", "SetPossibleRewards", "SetRewards", "SetTimeToGiveLootBox", "ShownRewards")),
|
SliceWiring("lootBoxes", "wpv_1", "aH", "ws.wrv", "ws.wrv(t, n.wpv_1)", listOf("AddKeys", "ClearRewards", "LootBoxOpeningFailed", "SelectCategory", "SetContainerData", "SetCurrentContainer", "SetGoToGarage", "SetIsAccelerated", "SetIsBlockOpenButtons", "SetIsOpening", "SetIsReopening", "SetIsShopVisited", "SetKeys", "SetPeriodOfGiveLootBox", "SetPossibleRewards", "SetRewards", "SetTimeToGiveLootBox", "ShownRewards")),
|
||||||
SliceWiring("notifications", "xpv_1", "?", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("?PJ", "?RJ", "AddNotification", "InitNotificationSound")),
|
SliceWiring("notifications", "xpv_1", "?", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("?PJ", "?RJ", "AddNotification", "InitNotificationSound")),
|
||||||
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("?_L", "?hL", "AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetLimits", "SetValidationStatus")),
|
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("?_L", "?hL", "AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetLimits", "SetValidationStatus")),
|
||||||
@@ -74,7 +74,7 @@ object RootWiring {
|
|||||||
SliceWiring("keyboardSettings", "dpx_1", "Sut", "bw.oth", "bw.oth(t, n.dpx_1)", listOf("?Iut", "?_C", "ChangeAction", "RemoveAction", "ResetAction")),
|
SliceWiring("keyboardSettings", "dpx_1", "Sut", "bw.oth", "bw.oth(t, n.dpx_1)", listOf("?Iut", "?_C", "ChangeAction", "RemoveAction", "ResetAction")),
|
||||||
SliceWiring("touchscreenHudSettings", "epx_1", "j7", "N7().xsv", "N7().xsv(t, n.epx_1)", listOf("?E7", "?_C", "UpdateSettings")),
|
SliceWiring("touchscreenHudSettings", "epx_1", "j7", "N7().xsv", "N7().xsv(t, n.epx_1)", listOf("?E7", "?_C", "UpdateSettings")),
|
||||||
SliceWiring("mainQuest", "fpx_1", "WZ", "ih.bsk", "ih.bsk(t, n.fpx_1)", listOf("?VZ", "AddRewardedQuest", "QuestsLoaded", "SetBattleUpdatedQuests", "SetEventPassShopItemId", "SetQuests", "UpdateQuests")),
|
SliceWiring("mainQuest", "fpx_1", "WZ", "ih.bsk", "ih.bsk(t, n.fpx_1)", listOf("?VZ", "AddRewardedQuest", "QuestsLoaded", "SetBattleUpdatedQuests", "SetEventPassShopItemId", "SetQuests", "UpdateQuests")),
|
||||||
SliceWiring("language", "gpx_1", "?", "m6().jtg", "m6().jtg(t, n.gpx_1)", listOf("?d6")),
|
SliceWiring("language", "gpx_1", "?", "m6().jtg", "m6().jtg(t, n.gpx_1)", listOf("SetLanguage")),
|
||||||
SliceWiring("launchParams", "hpx_1", "?", "(pass-through)", "n.hpx_1", listOf()),
|
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("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("entrance", "jpx_1", "hD", "Bv.pth", "Bv.pth(t, n.jpx_1)", listOf("HideSocialNetworks", "SetAgeWarnShow", "SetAuthorizedByChinaSdkToken", "SetClassic", "SetControlsForChina", "SetRunningInChinaDataCenter", "SetRunningInChinaPartners")),
|
||||||
@@ -82,7 +82,7 @@ object RootWiring {
|
|||||||
SliceWiring("spectator", "lpx_1", "HA", "Li.jqm", "Li.jqm(t, n.lpx_1)", listOf("?JA", "?XA", "?YA", "?ZA", "SetSpectator", "UpdateHudLayout")),
|
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("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("rulesUpdated", "npx_1", "i1", "Ch.dsm", "Ch.dsm(t, n.npx_1)", listOf("UpdateRules")),
|
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("?bC", "?qC", "Init", "SetSkipNextPromotedAnnounce")),
|
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("?qJ", "Init")),
|
SliceWiring("notificationByCountry", "ppx_1", "gJ", "R_.ssd", "R_.ssd(t, n.ppx_1)", listOf("?qJ", "Init")),
|
||||||
SliceWiring("userRenameState", "qpx_1", "O8", "Rf.pte", "Rf.pte(t, n.qpx_1)", listOf("?B8", "?F8", "?G8", "?Q8", "?U8", "ChangeUidInputState", "ResponseActual", "ResponseDelivered", "SetChangeUidParams", "SetRubiesForRename", "SetValidationStatus")),
|
SliceWiring("userRenameState", "qpx_1", "O8", "Rf.pte", "Rf.pte(t, n.qpx_1)", listOf("?B8", "?F8", "?G8", "?Q8", "?U8", "ChangeUidInputState", "ResponseActual", "ResponseDelivered", "SetChangeUidParams", "SetRubiesForRename", "SetValidationStatus")),
|
||||||
SliceWiring("eventGoods", "rpx_1", "W7", "Wc.esx", "Wc.esx(t, n.rpx_1)", listOf("InitEventGoods")),
|
SliceWiring("eventGoods", "rpx_1", "W7", "Wc.esx", "Wc.esx(t, n.rpx_1)", listOf("InitEventGoods")),
|
||||||
@@ -106,7 +106,7 @@ object RootWiring {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
|
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
|
||||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "launchParams")
|
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams")
|
||||||
|
|
||||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
||||||
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
||||||
// ⑧ 全目录动作类都能按 KClass 路由
|
// ⑧ 全目录动作类都能按 KClass 路由
|
||||||
// ⑨~㉔ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
// ⑨~㉘ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||||
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings;
|
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings /
|
||||||
|
// AccountSettings / ProBattle / Language / Announcement;
|
||||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||||
//
|
//
|
||||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||||
@@ -100,6 +101,13 @@ import App.State.SetRankNames
|
|||||||
import App.State.SetRubies
|
import App.State.SetRubies
|
||||||
import App.State.SetUserPropertiesAction
|
import App.State.SetUserPropertiesAction
|
||||||
import App.State.SetUserId
|
import App.State.SetUserId
|
||||||
|
import App.State.BattleTeam
|
||||||
|
import App.State.ClearInputUid
|
||||||
|
import App.State.Fight
|
||||||
|
import App.State.Language
|
||||||
|
import App.State.PasswordStatus
|
||||||
|
import App.State.SetAccountRemovalDays
|
||||||
|
import App.State.SetSkipNextPromotedAnnounce
|
||||||
import App.State.SettingsCategory
|
import App.State.SettingsCategory
|
||||||
import App.State.SpendCoins
|
import App.State.SpendCoins
|
||||||
import App.State.SpendCrystals
|
import App.State.SpendCrystals
|
||||||
@@ -268,8 +276,31 @@ import App.State.Reducers.DisableAccountSettings
|
|||||||
import App.State.Reducers.DisableLobbyTutorial
|
import App.State.Reducers.DisableLobbyTutorial
|
||||||
import App.State.Reducers.ReduceSettings
|
import App.State.Reducers.ReduceSettings
|
||||||
import App.State.Reducers.ReduceUser
|
import App.State.Reducers.ReduceUser
|
||||||
|
import App.State.Reducers.AccountSettingsStateSlice
|
||||||
|
import App.State.Reducers.AnnouncementStateSlice
|
||||||
|
import App.State.Reducers.ClearAccountRemovalTimer
|
||||||
|
import App.State.Reducers.DisableAnnouncement
|
||||||
|
import App.State.Reducers.ProBattleStateSlice
|
||||||
|
import App.State.Reducers.ReduceAccountSettings
|
||||||
|
import App.State.Reducers.ReduceAnnouncement
|
||||||
|
import App.State.Reducers.ReduceLanguage
|
||||||
|
import App.State.Reducers.ReduceProBattle
|
||||||
|
import App.State.Reducers.ResetPasswordFields
|
||||||
|
import App.State.Reducers.SetAnnouncementItems
|
||||||
|
import App.State.Reducers.SetAnnouncementModelLoaded
|
||||||
|
import App.State.Reducers.SetCountry
|
||||||
|
import App.State.Reducers.SetCurrentPassword
|
||||||
|
import App.State.Reducers.SetCurrentPasswordMandatory
|
||||||
|
import App.State.Reducers.SetEmail
|
||||||
|
import App.State.Reducers.SetEmailConfirmed
|
||||||
import App.State.Reducers.SetInDebt
|
import App.State.Reducers.SetInDebt
|
||||||
|
import App.State.Reducers.SetLanguage
|
||||||
|
import App.State.Reducers.SetNewPassword
|
||||||
|
import App.State.Reducers.SetPassword as SetBattlePassword
|
||||||
|
import App.State.Reducers.SetPasswordStatus
|
||||||
|
import App.State.Reducers.SetRepeatNewPassword
|
||||||
import App.State.Reducers.SetSettingsCategory
|
import App.State.Reducers.SetSettingsCategory
|
||||||
|
import App.State.Reducers.SetUnlockButtonForRenameUid
|
||||||
import App.State.Reducers.SettingsStateSlice
|
import App.State.Reducers.SettingsStateSlice
|
||||||
import App.State.Reducers.UserRankBounds
|
import App.State.Reducers.UserRankBounds
|
||||||
import App.State.Reducers.UserStateSlice
|
import App.State.Reducers.UserStateSlice
|
||||||
@@ -698,7 +729,7 @@ object StateCheck {
|
|||||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⑭ 根 reducer:89 个槽位的委派表 + 14 个已迁移切片(13 归约 + launchParams 透传)
|
// ⑭ 根 reducer:89 个槽位的委派表 + 18 个已迁移切片(17 归约 + launchParams 透传)
|
||||||
run {
|
run {
|
||||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||||
Check(
|
Check(
|
||||||
@@ -762,14 +793,15 @@ object StateCheck {
|
|||||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||||
)
|
)
|
||||||
Check(
|
Check(
|
||||||
"根:迁移表 = 14 个已迁移字段,其余 75 个待迁",
|
"根:迁移表 = 18 个已迁移字段,其余 71 个待迁",
|
||||||
RootReducer.Migrated ==
|
RootReducer.Migrated ==
|
||||||
setOf(
|
setOf(
|
||||||
"inviteState", "loginByPassword", "twitchState", "home",
|
"inviteState", "loginByPassword", "twitchState", "home",
|
||||||
"localBattleUserState", "shop", "screen", "lootBoxes", "garage",
|
"localBattleUserState", "shop", "screen", "lootBoxes", "garage",
|
||||||
"garagePreview", "entrance", "user", "settings", "launchParams",
|
"garagePreview", "entrance", "user", "settings", "accountSettings",
|
||||||
|
"proBattle", "language", "announcement", "launchParams",
|
||||||
) &&
|
) &&
|
||||||
RootWiring.Unmigrated.size == 75
|
RootWiring.Unmigrated.size == 71
|
||||||
)
|
)
|
||||||
|
|
||||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||||
@@ -825,7 +857,7 @@ object StateCheck {
|
|||||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||||
)
|
)
|
||||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 14/89")
|
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 18/89")
|
||||||
|
|
||||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||||
@@ -2692,6 +2724,207 @@ object StateCheck {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ㉕ 切片 reducer:AccountSettings(oet / Lp.mtg,13 字段;13 个 instanceof 臂)
|
||||||
|
run {
|
||||||
|
val S0 = AccountSettingsStateSlice()
|
||||||
|
Check(
|
||||||
|
"切片 AccountSettings:默认值完整",
|
||||||
|
!S0.IsCurrentPasswordMandatory && S0.CurrentPassword == "" &&
|
||||||
|
S0.NewPassword == "" && S0.RepeatNewPassword == "" &&
|
||||||
|
S0.NewPasswordStatus == PasswordStatus.NONE && S0.CountryCode == null &&
|
||||||
|
S0.CountryName == null && S0.Email == null && !S0.EmailConfirmed &&
|
||||||
|
!S0.LocationCheckEnabled && !S0.IsUnlockButtonForRenameUid &&
|
||||||
|
!S0.IsClearInputUid && S0.RemovalTimer == null
|
||||||
|
)
|
||||||
|
val Reset = ReduceAccountSettings(
|
||||||
|
S0.copy(
|
||||||
|
CurrentPassword = "old",
|
||||||
|
NewPassword = "new",
|
||||||
|
RepeatNewPassword = "new",
|
||||||
|
NewPasswordStatus = PasswordStatus.OK,
|
||||||
|
IsUnlockButtonForRenameUid = true,
|
||||||
|
Email = "keep@example.com",
|
||||||
|
RemovalTimer = 42L,
|
||||||
|
),
|
||||||
|
ResetPasswordFields,
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 AccountSettings:ResetPasswordFields 只清密码三件套/状态/解锁按钮",
|
||||||
|
Reset.CurrentPassword == "" && Reset.NewPassword == "" &&
|
||||||
|
Reset.RepeatNewPassword == "" && Reset.NewPasswordStatus == PasswordStatus.NONE &&
|
||||||
|
!Reset.IsUnlockButtonForRenameUid && Reset.Email == "keep@example.com" &&
|
||||||
|
Reset.RemovalTimer == 42L
|
||||||
|
)
|
||||||
|
val S1 = listOf<Any>(
|
||||||
|
SetCurrentPassword("cur"),
|
||||||
|
SetNewPassword("next"),
|
||||||
|
SetRepeatNewPassword("next"),
|
||||||
|
SetPasswordStatus(PasswordStatus.SIMPLE),
|
||||||
|
SetCurrentPasswordMandatory(true),
|
||||||
|
SetEmail("user@example.com"),
|
||||||
|
SetEmailConfirmed(true),
|
||||||
|
SetCountry("RU", "Russia"),
|
||||||
|
SetUnlockButtonForRenameUid(true),
|
||||||
|
ClearInputUid(true),
|
||||||
|
SetAccountRemovalDays(1_700_000_000_000L),
|
||||||
|
).fold(S0) { S, A -> ReduceAccountSettings(S, A) }
|
||||||
|
Check(
|
||||||
|
"切片 AccountSettings:11 个单字段动作按顺序写入",
|
||||||
|
S1.CurrentPassword == "cur" && S1.NewPassword == "next" &&
|
||||||
|
S1.RepeatNewPassword == "next" && S1.NewPasswordStatus == PasswordStatus.SIMPLE &&
|
||||||
|
S1.IsCurrentPasswordMandatory && S1.Email == "user@example.com" &&
|
||||||
|
S1.EmailConfirmed && S1.CountryCode == "RU" && S1.CountryName == "Russia" &&
|
||||||
|
S1.IsUnlockButtonForRenameUid && S1.IsClearInputUid &&
|
||||||
|
S1.RemovalTimer == 1_700_000_000_000L
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 AccountSettings:ClearAccountRemovalTimer 只清 removalTimer",
|
||||||
|
ReduceAccountSettings(S1, ClearAccountRemovalTimer).let {
|
||||||
|
it.RemovalTimer == null && it.Email == "user@example.com"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 AccountSettings:无关动作不改状态",
|
||||||
|
ReduceAccountSettings(S0, AddFrags(1)) == S0
|
||||||
|
)
|
||||||
|
|
||||||
|
val Root0 = RootState()
|
||||||
|
val Root1 = ReduceRoot(Root0, SetCurrentPassword("root-pass"))
|
||||||
|
Check(
|
||||||
|
"根:SetCurrentPassword 只动 accountSettings",
|
||||||
|
Root1.AccountSettings.CurrentPassword == "root-pass" &&
|
||||||
|
Root1.Language == Root0.Language && Root1.ProBattle == Root0.ProBattle
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"根:accountSettings 无关动作保留前一次归约",
|
||||||
|
ReduceRoot(Root1, AddFrags(1)).AccountSettings == Root1.AccountSettings
|
||||||
|
)
|
||||||
|
val W = RootWiring.ByField("accountSettings")!!
|
||||||
|
Check(
|
||||||
|
"根:accountSettings 委派到 oet / Lp.mtg 且 13 个动作全部具名",
|
||||||
|
W.SliceClass == "oet" && W.Reducer == "Lp.mtg" && W.Actions.size == 13 &&
|
||||||
|
W.Actions.containsAll(
|
||||||
|
setOf(
|
||||||
|
"ClearAccountRemovalTimer", "ClearInputUid", "ResetPasswordFields",
|
||||||
|
"SetAccountRemovalDays", "SetCountry", "SetCurrentPassword",
|
||||||
|
"SetCurrentPasswordMandatory", "SetEmail", "SetEmailConfirmed",
|
||||||
|
"SetNewPassword", "SetPasswordStatus", "SetRepeatNewPassword",
|
||||||
|
"SetUnlockButtonForRenameUid",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ㉖ 切片 reducer:ProBattle(Wtt / uv.wth,3 字段;2 个 instanceof 臂)
|
||||||
|
run {
|
||||||
|
val S0 = ProBattleStateSlice()
|
||||||
|
Check(
|
||||||
|
"切片 ProBattle:默认 -1 / NONE / 空密码表",
|
||||||
|
S0.LastFightBattleId == -1L && S0.LastFightBattleTeam == BattleTeam.NONE &&
|
||||||
|
S0.BattlePasswords.isEmpty()
|
||||||
|
)
|
||||||
|
val Fight1 = ReduceProBattle(S0, Fight(10L, BattleTeam.TEAM_A))
|
||||||
|
Check(
|
||||||
|
"切片 ProBattle:Fight 写上一场 id + team",
|
||||||
|
Fight1.LastFightBattleId == 10L && Fight1.LastFightBattleTeam == BattleTeam.TEAM_A
|
||||||
|
)
|
||||||
|
val WithPass = ReduceProBattle(Fight1, SetBattlePassword(10L, "secret"))
|
||||||
|
Check(
|
||||||
|
"切片 ProBattle:SetPassword 非空时写入密码表",
|
||||||
|
WithPass.BattlePasswords == mapOf(10L to "secret")
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ProBattle:SetPassword 空串时删除密码表项",
|
||||||
|
ReduceProBattle(WithPass, SetBattlePassword(10L, "")).BattlePasswords.isEmpty()
|
||||||
|
)
|
||||||
|
Check("切片 ProBattle:无关动作不改状态", ReduceProBattle(S0, AddFrags(1)) == S0)
|
||||||
|
|
||||||
|
val Root0 = RootState()
|
||||||
|
val Root1 = ReduceRoot(Root0, Fight(77L, BattleTeam.TEAM_B))
|
||||||
|
Check(
|
||||||
|
"根:Fight 只动 proBattle",
|
||||||
|
Root1.ProBattle.LastFightBattleId == 77L &&
|
||||||
|
Root1.ProBattle.LastFightBattleTeam == BattleTeam.TEAM_B &&
|
||||||
|
Root1.AccountSettings == Root0.AccountSettings
|
||||||
|
)
|
||||||
|
val W = RootWiring.ByField("proBattle")!!
|
||||||
|
Check(
|
||||||
|
"根:proBattle 委派到 Wtt / uv.wth 且 2 个动作",
|
||||||
|
W.SliceClass == "Wtt" && W.Reducer == "uv.wth" &&
|
||||||
|
W.Actions.toSet() == setOf("Fight", "SetPassword")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ㉗ 切片 reducer:Language(枚举值 / m6().jtg,1 个 instanceof 臂)
|
||||||
|
run {
|
||||||
|
Check("切片 Language:根状态默认 EN", RootState().Language == Language.EN)
|
||||||
|
Check(
|
||||||
|
"切片 Language:SetLanguage 写回枚举",
|
||||||
|
ReduceLanguage(Language.EN, SetLanguage(Language.ZH)) == Language.ZH
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 Language:无关动作保留当前语言",
|
||||||
|
ReduceLanguage(Language.ZH, AddFrags(1)) == Language.ZH
|
||||||
|
)
|
||||||
|
val Root0 = RootState()
|
||||||
|
val Root1 = ReduceRoot(Root0, SetLanguage(Language.DE))
|
||||||
|
Check(
|
||||||
|
"根:SetLanguage 只动 language",
|
||||||
|
Root1.Language == Language.DE && Root1.Announcement == Root0.Announcement
|
||||||
|
)
|
||||||
|
val W = RootWiring.ByField("language")!!
|
||||||
|
Check(
|
||||||
|
"根:language 委派到 m6().jtg 且动作具名",
|
||||||
|
W.SliceClass == "?" && W.Reducer == "m6().jtg" &&
|
||||||
|
W.Actions == listOf("SetLanguage")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ㉘ 切片 reducer:Announcement(lC / PC().rq5,5 字段;4 个 instanceof 臂)
|
||||||
|
run {
|
||||||
|
val S0 = AnnouncementStateSlice()
|
||||||
|
Check(
|
||||||
|
"切片 Announcement:默认值完整",
|
||||||
|
S0.IsEnabled && !S0.ModelLoaded && S0.Items.isEmpty() &&
|
||||||
|
!S0.WasInBattle && !S0.SkipNextPromotedAnnounce
|
||||||
|
)
|
||||||
|
val S1 = ReduceAnnouncement(
|
||||||
|
ReduceAnnouncement(
|
||||||
|
ReduceAnnouncement(
|
||||||
|
ReduceAnnouncement(S0, DisableAnnouncement),
|
||||||
|
SetAnnouncementModelLoaded,
|
||||||
|
),
|
||||||
|
SetAnnouncementItems(listOf("a", "b")),
|
||||||
|
),
|
||||||
|
SetSkipNextPromotedAnnounce(true),
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 Announcement:4 个臂分别写 enabled/modelLoaded/items/skip",
|
||||||
|
!S1.IsEnabled && S1.ModelLoaded && S1.Items == listOf<Any?>("a", "b") &&
|
||||||
|
S1.SkipNextPromotedAnnounce
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 Announcement:无关动作不改状态",
|
||||||
|
ReduceAnnouncement(S0, AddFrags(1)) == S0
|
||||||
|
)
|
||||||
|
|
||||||
|
val Root0 = RootState()
|
||||||
|
val Root1 = ReduceRoot(Root0, DisableAnnouncement)
|
||||||
|
Check(
|
||||||
|
"根:DisableAnnouncement 只动 announcement",
|
||||||
|
!Root1.Announcement.IsEnabled && Root1.AccountSettings == Root0.AccountSettings
|
||||||
|
)
|
||||||
|
val W = RootWiring.ByField("announcement")!!
|
||||||
|
Check(
|
||||||
|
"根:announcement 委派到 lC / PC().rq5 且 4 个动作全部具名",
|
||||||
|
W.SliceClass == "lC" && W.Reducer == "PC().rq5" &&
|
||||||
|
W.Actions.toSet() == setOf(
|
||||||
|
"DisableAnnouncement", "SetAnnouncementItems",
|
||||||
|
"SetAnnouncementModelLoaded", "SetSkipNextPromotedAnnounce",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val Failed = Results.filter { !it.second }
|
val Failed = Results.filter { !it.second }
|
||||||
println("TankiOffline 状态层自检:${Results.size} 项")
|
println("TankiOffline 状态层自检:${Results.size} 项")
|
||||||
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
||||||
|
|||||||
Reference in new issue
Block a user