feat(state): 迁移 matchmakingGroup
This commit is contained in:
1 parent
6f68afa678
commit
8b124388ec
8 files changed
+304
-21
No files matched your search
@@ -143,7 +143,7 @@
|
||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||
- 已迁移切片 **87/89**:
|
||||
- 已迁移切片 **88/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)
|
||||
@@ -439,12 +439,16 @@
|
||||
`AddItemNotification(s)` 加入(构造 `ItemNotificationData`);`RemoveItemNotification` 先按有效期过滤,
|
||||
再删掉匹配谓词的通知;`UpdateItemNotification` 对匹配项应用 handler;
|
||||
`S1` 只做持久化(no-op);`_C` 重载时只保留有效期内的通知。
|
||||
- `matchmakingGroup` → `rW` / `NW().is0`(`MatchmakingGroupReducer.kt`):4 字段,13 个分支。
|
||||
组员模型 `MatchmakingGroupMember(userId, isReady, mountItems)`(原包由 DI 服务构造,这里按 userId + 默认值收口);
|
||||
`AddUser`(与战斗列表同名)改名 `AddGroupMember`;邀请 / 接受 / 拒绝维护 pending 与 members;
|
||||
`UpdateUserReadiness` / `UpdateMountItems` 按 userId 命中后 copy;选中列表三臂 + `zW` 清空。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 86 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **2 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 87/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **964 项全绿**,含 89 槽位完整性、86 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 87 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **1 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 88/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **977 项全绿**,含 89 槽位完整性、87 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T14:12:51.294Z",
|
||||
"generatedAt": "2026-10-24T14:15:45.383Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -93,7 +93,8 @@
|
||||
"clansStorage",
|
||||
"clan",
|
||||
"chatState",
|
||||
"itemNotifications"
|
||||
"itemNotifications",
|
||||
"matchmakingGroup"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -1643,6 +1644,18 @@
|
||||
"Name": "PersistItemNotifications",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "通知列表变化后写回存储(纯 reducer 里 no-op)"
|
||||
},
|
||||
{
|
||||
"Local": "zW",
|
||||
"Name": "ResetMatchmakingGroup",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "members / pendingInvitationsUsers 清空"
|
||||
},
|
||||
{
|
||||
"Local": "lW",
|
||||
"Name": "AddGroupMember",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "按 userId 去重后追加组员(原名 AddUser,与战斗列表同名类冲突,改名 AddGroupMember)"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -2549,14 +2562,14 @@
|
||||
"Reducer": "NW().is0",
|
||||
"Delegate": "NW().is0(t, n.jpw_1)",
|
||||
"Actions": [
|
||||
"?zW",
|
||||
"AcceptedInviteToGroup",
|
||||
"AddUser",
|
||||
"AddGroupMember",
|
||||
"AddUserToSelected",
|
||||
"InitMatchmakingGroup",
|
||||
"Invite",
|
||||
"RejectedInviteToGroup",
|
||||
"RemoveUserToSelected",
|
||||
"ResetMatchmakingGroup",
|
||||
"SetAllowedFromFriends",
|
||||
"SetSelectedUsers",
|
||||
"UpdateMountItems",
|
||||
@@ -2564,7 +2577,7 @@
|
||||
"UserRemoved"
|
||||
],
|
||||
"Default": "new rW()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "shop",
|
||||
|
||||
@@ -370,6 +370,9 @@ const AnonymousActionNames = new Map([
|
||||
['YP', 'SetChatConfig'],
|
||||
// itemNotifications 切片(_1 / I1.dso):S1 没有 toString。
|
||||
['S1', 'PersistItemNotifications'],
|
||||
// matchmakingGroup 切片(rW / PW.is0):zW 没有 toString;lW 的 AddUser 与战斗列表同名类冲突。
|
||||
['zW', 'ResetMatchmakingGroup'],
|
||||
['lW', 'AddGroupMember'],
|
||||
]);
|
||||
|
||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||
@@ -861,6 +864,7 @@ const Migrated = {
|
||||
clan: { Type: 'ClanStateSlice', Reduce: 'ReduceClan' },
|
||||
chatState: { Type: 'ChatStateSlice', Reduce: 'ReduceChat' },
|
||||
itemNotifications: { Type: 'ItemNotificationsStateSlice', Reduce: 'ReduceItemNotifications' },
|
||||
matchmakingGroup: { Type: 'MatchmakingGroupStateSlice', Reduce: 'ReduceMatchmakingGroup' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -1310,6 +1314,8 @@ const AppSettingsAnonymousEffects = {
|
||||
sN: 'isFirstChannelMessagesLoad=false',
|
||||
YP: 'config(原名 Configure,与匹配大厅同名类冲突,改名 SetChatConfig)',
|
||||
S1: '通知列表变化后写回存储(纯 reducer 里 no-op)',
|
||||
zW: 'members / pendingInvitationsUsers 清空',
|
||||
lW: '按 userId 去重后追加组员(原名 AddUser,与战斗列表同名类冲突,改名 AddGroupMember)',
|
||||
};
|
||||
fs.writeFileSync(
|
||||
path.join(P, 'names', 'root_wiring.json'),
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// MatchmakingGroupReducer.kt —— 匹配组队切片(对应 module_10032 的 rW 状态 + PW.is0)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 rW:4 个字段,toString 名 "MatchmakingGroup(…)"。
|
||||
// 默认:members=emptyList()、allowedFromFriends=emptySet()、pendingInvitationsUsers=emptySet()、
|
||||
// selectedPlayersSendInvite=emptySet()。
|
||||
// 根委派:`NW().is0(t, n.jpw_1)`。
|
||||
// reducer(13 个实例臂):
|
||||
// AW(InitMatchmakingGroup) → members(整体替换)
|
||||
// cW(SetAllowedFromFriends) → allowedFromFriends
|
||||
// lW(AddUser) → 按 userId 去重后追加组员
|
||||
// fW(UserRemoved) → 按 userId 移除
|
||||
// zW(无 toString) → members / pending 清空
|
||||
// dW(Invite) → pending += userId
|
||||
// TW(AcceptedInviteToGroup) → 加入组员、pending -= userId
|
||||
// EW(RejectedInviteToGroup) → pending -= userId
|
||||
// bW(UpdateMountItems) → 命中 userId 的组员写 mountItems
|
||||
// qW(UpdateUserReadiness) → 命中 userId 的组员写 isReady
|
||||
// yW / $W(AddUserToSelected / RemoveUserToSelected)→ selected += / -=
|
||||
// xW(SetSelectedUsers) → selected(整体替换)
|
||||
//
|
||||
// 组员模型 `MatchmakingGroupMember(userId, isReady, mountItems)`:原包由 DI 服务构造
|
||||
// (`hW(r).t2e(...)`),离线重建按「userId + 默认值」收口。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.AcceptedInviteToGroup
|
||||
import App.State.AddUserToSelected
|
||||
import App.State.InitMatchmakingGroup
|
||||
import App.State.Invite
|
||||
import App.State.RejectedInviteToGroup
|
||||
import App.State.RemoveUserToSelected
|
||||
import App.State.SetAllowedFromFriends
|
||||
import App.State.SetSelectedUsers
|
||||
import App.State.UpdateMountItems
|
||||
import App.State.UpdateUserReadiness
|
||||
import App.State.UserRemoved
|
||||
|
||||
/* ------------------------------------------------------------------ 模型 */
|
||||
|
||||
data class MatchmakingGroupMember(
|
||||
val UserId: Any?,
|
||||
val IsReady: Boolean = false,
|
||||
val MountItems: Any? = null,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `zW`:清空组员与待处理邀请。 */
|
||||
object ResetMatchmakingGroup
|
||||
|
||||
/* ------------------------------------------------------------------ 动作(原名冲突) */
|
||||
|
||||
/** 原 `lW`(原名 `AddUser`,与战斗列表的同名类冲突)。 */
|
||||
data class AddGroupMember(val user: Any?)
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class MatchmakingGroupStateSlice(
|
||||
val Members: List<Any?> = emptyList(),
|
||||
val AllowedFromFriends: Set<Any?> = emptySet(),
|
||||
val PendingInvitationsUsers: Set<Any?> = emptySet(),
|
||||
val SelectedPlayersSendInvite: Set<Any?> = emptySet(),
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
private fun AsMember(Value: Any?): MatchmakingGroupMember? = Value as? MatchmakingGroupMember
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun AsSet(Value: Any?): Set<Any?> = when (Value) {
|
||||
is Iterable<*> -> Value.toSet()
|
||||
is Array<*> -> Value.toSet()
|
||||
else -> emptySet()
|
||||
}
|
||||
|
||||
private fun MapMember(
|
||||
Members: List<Any?>,
|
||||
UserId: Any?,
|
||||
Transform: (MatchmakingGroupMember) -> MatchmakingGroupMember,
|
||||
): List<Any?> = Members.map {
|
||||
val Member = AsMember(it)
|
||||
if (Member != null && Member.UserId == UserId) Transform(Member) else it
|
||||
}
|
||||
|
||||
private fun AddMember(Members: List<Any?>, UserId: Any?): List<Any?> =
|
||||
Members.filterNot { AsMember(it)?.UserId == UserId } + MatchmakingGroupMember(UserId)
|
||||
|
||||
fun ReduceMatchmakingGroup(
|
||||
State0: MatchmakingGroupStateSlice,
|
||||
Action: Any,
|
||||
): MatchmakingGroupStateSlice = when (Action) {
|
||||
is InitMatchmakingGroup ->
|
||||
State0.copy(Members = (Action.members as? List<Any?>) ?: emptyList())
|
||||
is SetAllowedFromFriends ->
|
||||
State0.copy(AllowedFromFriends = AsSet(Action.allowedFromFriends))
|
||||
is AddGroupMember -> {
|
||||
val Member = AsMember(Action.user)
|
||||
if (Member == null) State0
|
||||
else State0.copy(
|
||||
Members = State0.Members.filterNot { AsMember(it)?.UserId == Member.UserId } + Member,
|
||||
)
|
||||
}
|
||||
is UserRemoved -> State0.copy(
|
||||
Members = State0.Members.filterNot { AsMember(it)?.UserId == Action.userId },
|
||||
)
|
||||
is ResetMatchmakingGroup -> State0.copy(
|
||||
Members = emptyList(),
|
||||
PendingInvitationsUsers = emptySet(),
|
||||
)
|
||||
is Invite -> State0.copy(
|
||||
PendingInvitationsUsers = State0.PendingInvitationsUsers + Action.userId,
|
||||
)
|
||||
is AcceptedInviteToGroup -> State0.copy(
|
||||
Members = AddMember(State0.Members, Action.userId),
|
||||
PendingInvitationsUsers = State0.PendingInvitationsUsers - Action.userId,
|
||||
)
|
||||
is RejectedInviteToGroup -> State0.copy(
|
||||
PendingInvitationsUsers = State0.PendingInvitationsUsers - Action.userId,
|
||||
)
|
||||
is UpdateMountItems -> {
|
||||
val Data = Action.mountItemsUserData as? Map<Any?, Any?> ?: emptyMap()
|
||||
State0.copy(
|
||||
Members = State0.Members.map {
|
||||
val Member = AsMember(it)
|
||||
if (Member != null && Data.containsKey(Member.UserId)) {
|
||||
Member.copy(MountItems = Data[Member.UserId])
|
||||
} else it
|
||||
},
|
||||
)
|
||||
}
|
||||
is UpdateUserReadiness -> State0.copy(
|
||||
Members = MapMember(State0.Members, Action.userId) {
|
||||
it.copy(IsReady = Action.isReady == true)
|
||||
},
|
||||
)
|
||||
is AddUserToSelected -> State0.copy(
|
||||
SelectedPlayersSendInvite = State0.SelectedPlayersSendInvite + Action.userId,
|
||||
)
|
||||
is RemoveUserToSelected -> State0.copy(
|
||||
SelectedPlayersSendInvite = State0.SelectedPlayersSendInvite - Action.usersId,
|
||||
)
|
||||
is SetSelectedUsers -> State0.copy(
|
||||
SelectedPlayersSendInvite = AsSet(Action.userIds),
|
||||
)
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 87 个切片里,86 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 88 个切片里,87 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -89,9 +89,10 @@
|
||||
// clan → ReduceClan(jN / we.hr0,根委派 `we.hr0(t, n.qpv_1)`)
|
||||
// chatState → ReduceChat(LP / $r.wqv,根委派 `$r.wqv(t, n.opv_1)`)
|
||||
// itemNotifications → ReduceItemNotifications(_1 / A1().dso,根委派 `A1().dso(t, n.xpx_1)`)
|
||||
// matchmakingGroup → ReduceMatchmakingGroup(rW / NW().is0,根委派 `NW().is0(t, n.jpw_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 2 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 1 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -141,6 +142,7 @@ import App.State.Reducers.ReduceLoginByPassword
|
||||
import App.State.Reducers.ReduceLootBoxes
|
||||
import App.State.Reducers.ReduceMainQuest
|
||||
import App.State.Reducers.ReduceMatchmaking
|
||||
import App.State.Reducers.ReduceMatchmakingGroup
|
||||
import App.State.Reducers.ReduceMaps
|
||||
import App.State.Reducers.ReduceAvailabilityByRank
|
||||
import App.State.Reducers.ReduceLobbyLoader
|
||||
@@ -282,6 +284,7 @@ object RootReducer {
|
||||
Clan = ReduceClan(State0.Clan, Action),
|
||||
ChatState = ReduceChat(State0.ChatState, Action),
|
||||
ItemNotifications = ReduceItemNotifications(State0.ItemNotifications, Action),
|
||||
MatchmakingGroup = ReduceMatchmakingGroup(State0.MatchmakingGroup, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import App.State.Reducers.LootBoxesStateSlice
|
||||
import App.State.Reducers.MainQuestStateSlice
|
||||
import App.State.Reducers.MapsStateSlice
|
||||
import App.State.Reducers.MatchmakingStateSlice
|
||||
import App.State.Reducers.MatchmakingGroupStateSlice
|
||||
import App.State.Reducers.MigrationStateSlice
|
||||
import App.State.Reducers.ModalScreenStateSlice
|
||||
import App.State.Reducers.ModerationStateSlice
|
||||
@@ -169,8 +170,8 @@ data class RootState(
|
||||
val BattleUsers: BattleUsersStateSlice = BattleUsersStateSlice(),
|
||||
/** 原 suppliesState(wE / $t.fq9)。已迁移。 */
|
||||
val SuppliesState: SuppliesStateSlice = SuppliesStateSlice(),
|
||||
/** 原 matchmakingGroup(rW / NW().is0)。未迁移:原样透传。 */
|
||||
val MatchmakingGroup: Any? = null,
|
||||
/** 原 matchmakingGroup(rW / NW().is0)。已迁移。 */
|
||||
val MatchmakingGroup: MatchmakingGroupStateSlice = MatchmakingGroupStateSlice(),
|
||||
/** 原 shop(j4 / (inline@shop))。已迁移。 */
|
||||
val Shop: ShopStateSlice = ShopStateSlice(),
|
||||
/** 原 battlePauseState(dT / NT().mqc)。已迁移。 */
|
||||
|
||||
@@ -51,7 +51,7 @@ object RootWiring {
|
||||
SliceWiring("battleResult", "gpw_1", "sR", "Fi.aqp", "Fi.aqp(t, n.gpw_1)", listOf("DoInit", "ResetBattleRewards", "SetHasBattleResult", "SetIncreasedReward", "SetRewards", "SetSimpleReward")),
|
||||
SliceWiring("battleUsers", "hpw_1", "uP", "sr.hqs", "sr.hqs(t, n.hpw_1)", listOf("AddBattleUser", "AddBattleUsers", "InitSuspiciousState", "InitUserLevels", "MigrateBattleUserTeams", "RemoveOnlineUser", "RemoveTankInfo", "ResetBattleUsers", "SetGearScore", "SetTankInfo", "SetTankResistance", "SetUserPremium", "UpdateUserLevel", "UpdateUserRank", "UpdateUserScoreKillsAndDeaths", "UpdateUserSuspiciousState", "UpdateUsersClanTag", "UpdateUsersScoreKillsAndDeaths")),
|
||||
SliceWiring("suppliesState", "ipw_1", "wE", "\$t.fq9", "\$t.fq9(t, n.ipw_1)", listOf("SetSupplyCount")),
|
||||
SliceWiring("matchmakingGroup", "jpw_1", "rW", "NW().is0", "NW().is0(t, n.jpw_1)", listOf("?zW", "AcceptedInviteToGroup", "AddUser", "AddUserToSelected", "InitMatchmakingGroup", "Invite", "RejectedInviteToGroup", "RemoveUserToSelected", "SetAllowedFromFriends", "SetSelectedUsers", "UpdateMountItems", "UpdateUserReadiness", "UserRemoved")),
|
||||
SliceWiring("matchmakingGroup", "jpw_1", "rW", "NW().is0", "NW().is0(t, n.jpw_1)", listOf("AcceptedInviteToGroup", "AddGroupMember", "AddUserToSelected", "InitMatchmakingGroup", "Invite", "RejectedInviteToGroup", "RemoveUserToSelected", "ResetMatchmakingGroup", "SetAllowedFromFriends", "SetSelectedUsers", "UpdateMountItems", "UpdateUserReadiness", "UserRemoved")),
|
||||
SliceWiring("shop", "kpw_1", "j4", "(inline@shop)", "(function (t, n) { var i; if (t instanceof w4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, t.lt6_1); else if (t instanceof Yut) i = n.qt6(cd, cd, cd, cd, !0); else if (t instanceof F5) i = new j4(cd, cd, cd, cd, cd, cd, cd, n.qq3_1); else if (t instanceof h4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.st5_1); else if (t instanceof c4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ut5_1); else if (t instanceof u4) i = n.qt6( cd, cd, cd, cd, cd, cd, …", listOf("?x4", "AddCategoriesWithBonus", "AddItemToOrderProcessing", "BlockBackButton", "BraintreeLoadingInterrupt", "ClearPreviousScreen", "ClearTargetCategory", "ClearTargetProductId", "CurrentPayMode", "DeleteOneTimePurchaseItem", "InitAdditionalDescription", "InitAmountString", "InitCategory", "InitCoinPackage", "InitCompensation", "InitCrystalKitPackage", "InitFeatureItem", "InitGroup", "InitItemViewType", "InitKeysPackage", "InitKitBundle", "InitKitPackage", "InitLootBoxAndPaint", "InitOneTimePurchaseItem", "InitPayModeAction", "InitPaymentModeAsShopItem", "InitPaymentProviderItems", "InitPaymentViewType", "InitRestrictionByPayMode", "InitRewardedVideosBlocks", "InitShopCard", "InitShopCardDiscount", "InitShopCardView", "InitShopItem", "InitShopItemName", "InitSingleItemKit", "InitSpecialKitPackage", "MobilePaymentStateChanged", "PaymentProviderFinishFail", "PaymentProviderFinishSuccess", "PaymentProviderInProgress", "PaymentUrlReceiveFailed", "PaymentUrlReceived", "RemoveDiscountItemForItem", "RemoveItemFromOrderProcessing", "ResetPaymentState", "ResetShop", "SavePreviousScreen", "SaveTargetProductId", "SaveTargetVisitedCategory", "SelectCategory", "SelectCategoryId", "SelectItem", "SetBraintreeToken", "SetCoinPaymentProviderUrl", "SetGoToHomeAfterBuy", "SetInitShopItemId", "SetPurchaseCompleted", "SetStripeIntentParams", "SetupDiscountCategoryByItem", "SetupDiscountItem", "SetupDiscountItemForItem", "ShopCardExpired", "ShopCardRemoved", "ShopCardSold", "ShopLoaded", "ToggleShopActivity", "TryToBuyOneTimePurchase", "UnblockBackButton")),
|
||||
SliceWiring("battlePauseState", "lpw_1", "dT", "NT().mqc", "NT().mqc(t, n.lpw_1)", listOf("CancelAutoPause", "OnPauseMode", "ResetBattlePause", "SetIdleKickPeriod")),
|
||||
SliceWiring("battleViewState", "mpw_1", "DT", "sI.jqd", "(null == \$n && new sI(), \$n).jqd(t, n.mpw_1)", listOf("ClearInvisibleElements", "DisableBattleInput", "DisableBattleMenu", "DisableBattleStatistics", "EnableBattleInput", "EnableBattleMenu", "EnableBattleStatistics", "EnterBackground", "HideBattleView", "LeaveBackground", "ResetBattleView", "SetElementsVisibility", "SetVisibility", "ShowBattleView")),
|
||||
@@ -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", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications")
|
||||
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", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications", "matchmakingGroup")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
|
||||
// Moderation / MigrationState / Register / UserReports /
|
||||
// Friends / Supplies / ClanUserInfo / BattleInvite / Partner / UserRename /
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage / Clan / ChatState / ItemNotifications;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage / Clan / ChatState / ItemNotifications / MatchmakingGroup;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -912,6 +912,22 @@ import App.State.UpdateItemNotification
|
||||
import App.State.Reducers.ItemNotificationsStateSlice
|
||||
import App.State.Reducers.PersistItemNotifications
|
||||
import App.State.Reducers.ReduceItemNotifications
|
||||
import App.State.AcceptedInviteToGroup
|
||||
import App.State.AddUserToSelected
|
||||
import App.State.InitMatchmakingGroup
|
||||
import App.State.Invite
|
||||
import App.State.RejectedInviteToGroup
|
||||
import App.State.RemoveUserToSelected
|
||||
import App.State.SetAllowedFromFriends
|
||||
import App.State.SetSelectedUsers
|
||||
import App.State.UpdateMountItems
|
||||
import App.State.UpdateUserReadiness
|
||||
import App.State.UserRemoved
|
||||
import App.State.Reducers.AddGroupMember
|
||||
import App.State.Reducers.MatchmakingGroupMember
|
||||
import App.State.Reducers.MatchmakingGroupStateSlice
|
||||
import App.State.Reducers.ReduceMatchmakingGroup
|
||||
import App.State.Reducers.ResetMatchmakingGroup
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1339,7 +1355,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 87 个已迁移切片(86 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 88 个已迁移切片(87 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1403,7 +1419,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 87 个已迁移字段,其余 2 个待迁",
|
||||
"根:迁移表 = 88 个已迁移字段,其余 1 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1428,8 +1444,9 @@ object StateCheck {
|
||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
||||
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState", "itemNotifications",
|
||||
"matchmakingGroup",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 2
|
||||
RootWiring.Unmigrated.size == 1
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1485,7 +1502,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 87/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 88/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -7431,6 +7448,99 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 98 切片 reducer:MatchmakingGroup(rW / NW().is0,4 字段;13 个动作)
|
||||
run {
|
||||
fun Member(Id: Any?, Ready: Boolean = false): MatchmakingGroupMember =
|
||||
MatchmakingGroupMember(UserId = Id, IsReady = Ready)
|
||||
|
||||
val S0 = MatchmakingGroupStateSlice()
|
||||
Check(
|
||||
"切片 MatchmakingGroup:默认值完整",
|
||||
S0.Members.isEmpty() && S0.AllowedFromFriends.isEmpty() &&
|
||||
S0.PendingInvitationsUsers.isEmpty() &&
|
||||
S0.SelectedPlayersSendInvite.isEmpty()
|
||||
)
|
||||
val S1 = ReduceMatchmakingGroup(S0, InitMatchmakingGroup(listOf(Member(1L))))
|
||||
Check("切片 MatchmakingGroup:InitMatchmakingGroup 整体替换", S1.Members.size == 1)
|
||||
val S2 = ReduceMatchmakingGroup(S1, AddGroupMember(Member(2L)))
|
||||
Check(
|
||||
"切片 MatchmakingGroup:AddGroupMember 追加组员",
|
||||
S2.Members.size == 2
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:AddGroupMember 按 userId 去重",
|
||||
ReduceMatchmakingGroup(S2, AddGroupMember(Member(2L, true))).let {
|
||||
it.Members.size == 2 &&
|
||||
(it.Members.last() as MatchmakingGroupMember).IsReady
|
||||
}
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:UserRemoved 删组员",
|
||||
ReduceMatchmakingGroup(S2, UserRemoved(1L)).Members.size == 1
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:邀请 / 接受 / 拒绝",
|
||||
ReduceMatchmakingGroup(S2, Invite(3L))
|
||||
.PendingInvitationsUsers == setOf<Any?>(3L) &&
|
||||
ReduceMatchmakingGroup(
|
||||
ReduceMatchmakingGroup(S2, Invite(3L)),
|
||||
AcceptedInviteToGroup(3L),
|
||||
).let {
|
||||
it.PendingInvitationsUsers.isEmpty() &&
|
||||
it.Members.any { M -> (M as MatchmakingGroupMember).UserId == 3L }
|
||||
} &&
|
||||
ReduceMatchmakingGroup(
|
||||
ReduceMatchmakingGroup(S2, Invite(4L)),
|
||||
RejectedInviteToGroup(4L, "no"),
|
||||
).PendingInvitationsUsers.isEmpty()
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:UpdateUserReadiness 命中 copy",
|
||||
(ReduceMatchmakingGroup(S2, UpdateUserReadiness(1L, true))
|
||||
.Members.first() as MatchmakingGroupMember).IsReady
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:UpdateMountItems 按 userId 写挂件",
|
||||
(ReduceMatchmakingGroup(S2, UpdateMountItems(mapOf<Any?, Any?>(1L to "mount")))
|
||||
.Members.first() as MatchmakingGroupMember).MountItems == "mount"
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:选中列表三臂",
|
||||
ReduceMatchmakingGroup(S2, AddUserToSelected(5L))
|
||||
.SelectedPlayersSendInvite == setOf<Any?>(5L) &&
|
||||
ReduceMatchmakingGroup(
|
||||
ReduceMatchmakingGroup(S2, AddUserToSelected(5L)),
|
||||
RemoveUserToSelected(5L),
|
||||
).SelectedPlayersSendInvite.isEmpty() &&
|
||||
ReduceMatchmakingGroup(S2, SetSelectedUsers(listOf(6L, 7L)))
|
||||
.SelectedPlayersSendInvite == setOf<Any?>(6L, 7L)
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:SetAllowedFromFriends / Reset",
|
||||
ReduceMatchmakingGroup(
|
||||
ReduceMatchmakingGroup(S2, SetAllowedFromFriends(listOf(1L))),
|
||||
ResetMatchmakingGroup,
|
||||
).let { it.AllowedFromFriends == setOf<Any?>(1L) && it.Members.isEmpty() }
|
||||
)
|
||||
Check(
|
||||
"切片 MatchmakingGroup:无关动作不改状态",
|
||||
ReduceMatchmakingGroup(S2, AddFrags(1)) == S2
|
||||
)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, Invite(9L))
|
||||
Check(
|
||||
"根:Invite 只动 matchmakingGroup",
|
||||
Root1.MatchmakingGroup.PendingInvitationsUsers == setOf<Any?>(9L) &&
|
||||
Root1.ItemNotifications == Root0.ItemNotifications
|
||||
)
|
||||
val W = RootWiring.ByField("matchmakingGroup")!!
|
||||
Check(
|
||||
"根:matchmakingGroup 委派到 rW / NW().is0 且 13 个动作",
|
||||
W.SliceClass == "rW" && W.Reducer == "NW().is0" && W.Actions.size == 13
|
||||
)
|
||||
}
|
||||
|
||||
val Failed = Results.filter { !it.second }
|
||||
println("TankiOffline 状态层自检:${Results.size} 项")
|
||||
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
||||
|
||||
Reference in new issue
Block a user