feat(state): 迁移 battlesSelect(复用 BattleParams/BattleInfoUser)
This commit is contained in:
1 parent
71aa9a5256
commit
cd597b4693
8 files changed
+434
-23
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` 动作名。
|
||||
- 已迁移切片 **80/89**:
|
||||
- 已迁移切片 **81/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)
|
||||
@@ -403,12 +403,18 @@
|
||||
`WasRegistered` 写等待时间与模式并清 `requestSent`;`EnterToBattleApply` 同时置两个进行中位;
|
||||
`WH`→`ResetMatchmakingProgress`、`tW`/`nW`→`StartLoadingBattle`/`CancelLoadingBattle`;
|
||||
模式表复用生成物 `MatchmakingModeRank`,`rankIndexToMinGS` 收口为 `Map<String, Int>`。
|
||||
- `battlesSelect` → `fA` / `Mi.vqk`(`BattlesSelectReducer.kt`):15 字段,20 个实例臂。
|
||||
`AddBattle` 把 `BattleCreateParameters`(+ suspicionLevel)转成 `BattleParams` 并建 users / score 空表;
|
||||
`AddUsers` 批量写 users / usersTeam 并同步个人分与可疑标记(`AddUser` 复用它);
|
||||
`SwapTeam` 换队并把该战斗内用户分数归零;`RemoveBattle` / `RemoveUser` 清对应的四张子表;
|
||||
其余为名字 / 可疑度 / 队伍分 / 个人分 / 资金 / 奖励上限 / 观战标记。
|
||||
模型全部复用生成物:`BattleParams` / `BattleInfoUser` / `BattleCreateParameters` / `ProBattleRewardLimitData`。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 79 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **9 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 80/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **871 项全绿**,含 89 槽位完整性、79 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 80 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **8 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 81/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **888 项全绿**,含 89 槽位完整性、80 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T12:37:28.603Z",
|
||||
"generatedAt": "2026-10-24T12:57:54.249Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -86,7 +86,8 @@
|
||||
"maps",
|
||||
"battleChat",
|
||||
"remoteUserDataStorage",
|
||||
"matchmaking"
|
||||
"matchmaking",
|
||||
"battlesSelect"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -1432,6 +1433,18 @@
|
||||
"Name": "CancelLoadingBattle",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "startLoadingBattle=false(`n.brw(cd, cd, cd, cd, !1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "gA",
|
||||
"Name": "MarkBattlesLoaded",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "loaded=true(`n.hqj(!0)`)"
|
||||
},
|
||||
{
|
||||
"Local": "dA",
|
||||
"Name": "ResetBattlesSelect",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "new fA() 整片复位"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -2156,8 +2169,6 @@
|
||||
"Reducer": "Mi.vqk",
|
||||
"Delegate": "Mi.vqk(t, n.apw_1)",
|
||||
"Actions": [
|
||||
"?dA",
|
||||
"?gA",
|
||||
"AddBattle",
|
||||
"AddUser",
|
||||
"AddUsers",
|
||||
@@ -2165,8 +2176,10 @@
|
||||
"EnterToBattleInfo",
|
||||
"InitProBattleRewardLimit",
|
||||
"LimitReached",
|
||||
"MarkBattlesLoaded",
|
||||
"RemoveBattle",
|
||||
"RemoveUser",
|
||||
"ResetBattlesSelect",
|
||||
"RoundFinish",
|
||||
"RoundStarted",
|
||||
"SetReEnterProtectBattleId",
|
||||
@@ -2178,7 +2191,7 @@
|
||||
"UpdateUserSuspicion"
|
||||
],
|
||||
"Default": "new fA()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "maps",
|
||||
|
||||
@@ -327,6 +327,9 @@ const AnonymousActionNames = new Map([
|
||||
['WH', 'ResetMatchmakingProgress'],
|
||||
['tW', 'StartLoadingBattle'],
|
||||
['nW', 'CancelLoadingBattle'],
|
||||
// battlesSelect 切片(fA / MA.vqk):gA/dA 没有 toString。
|
||||
['gA', 'MarkBattlesLoaded'],
|
||||
['dA', 'ResetBattlesSelect'],
|
||||
]);
|
||||
|
||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||
@@ -811,6 +814,7 @@ const Migrated = {
|
||||
Reduce: 'ReduceRemoteUserDataStorage',
|
||||
},
|
||||
matchmaking: { Type: 'MatchmakingStateSlice', Reduce: 'ReduceMatchmaking' },
|
||||
battlesSelect: { Type: 'BattlesSelectStateSlice', Reduce: 'ReduceBattlesSelect' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -1226,8 +1230,9 @@ const AppSettingsAnonymousEffects = {
|
||||
WH: 'matchmakingAndLoadingBattleInProgress=false、startLoadingBattle=false、requestSent=false',
|
||||
tW: 'startLoadingBattle=true(`n.brw(cd, cd, cd, cd, !0)`)',
|
||||
nW: 'startLoadingBattle=false(`n.brw(cd, cd, cd, cd, !1)`)',
|
||||
gA: 'loaded=true(`n.hqj(!0)`)',
|
||||
dA: 'new fA() 整片复位',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(P, 'names', 'root_wiring.json'),
|
||||
JSON.stringify(
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
// BattlesSelectReducer.kt —— 战斗列表切片(对应 module_10032 的 fA 状态 + MA.vqk)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 fA:15 个字段,toString 名 "BattlesSelect(…)";copy 是 hqj(15 字段 + 回调)。
|
||||
// 根委派:`Mi.vqk(t, n.apw_1)`。
|
||||
// reducer(20 个实例臂):
|
||||
// TA(RoundStarted) → roundEndTimes[battleId] = 当前时间 + 剩余秒;score[battleId] 清空
|
||||
// IA(SwapTeam) → 该战斗内所有用户交换队伍,并把他们的个人分数归零
|
||||
// AA(RoundFinish) → roundEndTimes / fund 删掉该战斗
|
||||
// gA(无 toString) → loaded=true
|
||||
// mA(AddBattle) → battles[battleId]=BattleParams;users / score 建空表;creators 写创建者
|
||||
// kA(RemoveBattle) → battles / users / creators / alreadySeeBattleInfo 删该战斗
|
||||
// zA(ChangeName) → battles[battleId].name
|
||||
// bA(UpdateSuspicion) → battles[battleId].suspicionLevel
|
||||
// yA(AddUsers) → 批量写 users / usersTeam(`$A` AddUser 复用它)
|
||||
// xA(RemoveUser) → users / usersTeam / userScore / userSuspicion / userSuspicionByIp 删该用户
|
||||
// jA(UpdateUserScore) → userScore[userId]
|
||||
// CA(UpdateUserSuspicion)→ userSuspicion / userSuspicionByIp
|
||||
// qA(UpdateTeamScore) → score[battleId][team]
|
||||
// dA(无 toString) → `new fA()` 整片复位
|
||||
// RA(EnterToBattleInfo) → alreadySeeBattleInfo += battleId
|
||||
// SA(UpdateFund) → fund[battleId]
|
||||
// EA(InitProBattleRewardLimit)→ proLimit
|
||||
// PA(LimitReached) → isLimitReached
|
||||
// NA(SetReEnterProtectBattleId)→ reEnterProtectBattleId
|
||||
//
|
||||
// 模型直接复用生成物:`BattleParams`(战斗描述)、`BattleInfoUser`(列表里的用户)、
|
||||
// `BattleCreateParameters`(AddBattle 载荷)、`ProBattleRewardLimitData`。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.AddBattle
|
||||
import App.State.AddUser
|
||||
import App.State.AddUsers
|
||||
import App.State.BattleCreateParameters
|
||||
import App.State.BattleInfoUser
|
||||
import App.State.BattleParams
|
||||
import App.State.ChangeName
|
||||
import App.State.EnterToBattleInfo
|
||||
import App.State.InitProBattleRewardLimit
|
||||
import App.State.LimitReached
|
||||
import App.State.ProBattleRewardLimitData
|
||||
import App.State.RemoveBattle
|
||||
import App.State.RemoveUser
|
||||
import App.State.RoundFinish
|
||||
import App.State.RoundStarted
|
||||
import App.State.SetReEnterProtectBattleId
|
||||
import App.State.SwapTeam
|
||||
import App.State.UpdateFund
|
||||
import App.State.UpdateSuspicion
|
||||
import App.State.UpdateTeamScore
|
||||
import App.State.UpdateUserScore
|
||||
import App.State.UpdateUserSuspicion
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `gA`:战斗列表已加载。 */
|
||||
object MarkBattlesLoaded
|
||||
|
||||
/** 原 `dA`:整片复位(`new fA()`)。 */
|
||||
object ResetBattlesSelect
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class BattlesSelectStateSlice(
|
||||
val Loaded: Boolean = false,
|
||||
val Battles: Map<Any?, Any?> = emptyMap(),
|
||||
val Score: Map<Any?, Any?> = emptyMap(),
|
||||
val Users: Map<Any?, Any?> = emptyMap(),
|
||||
val Creators: Map<Any?, Any?> = emptyMap(),
|
||||
val UsersTeam: Map<Any?, Any?> = emptyMap(),
|
||||
val UserScore: Map<Any?, Any?> = emptyMap(),
|
||||
val UserSuspicion: Map<Any?, Any?> = emptyMap(),
|
||||
val UserSuspicionByIp: Map<Any?, Any?> = emptyMap(),
|
||||
val RoundEndTimes: Map<Any?, Any?> = emptyMap(),
|
||||
val AlreadySeeBattleInfo: Set<Any?> = emptySet(),
|
||||
val Fund: Map<Any?, Any?> = emptyMap(),
|
||||
val ProLimit: ProBattleRewardLimitData? = null,
|
||||
val IsLimitReached: Boolean = false,
|
||||
val ReEnterProtectBattleId: Any? = null,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun Inner(Map: Map<Any?, Any?>, Key: Any?): Map<Any?, Any?> =
|
||||
Map[Key] as? Map<Any?, Any?> ?: emptyMap()
|
||||
|
||||
/** `BattleCreateParameters` → 列表用的 `BattleParams`(原 `new vA(...)`)。 */
|
||||
private fun ParamsOf(
|
||||
Params: BattleCreateParameters,
|
||||
SuspicionLevel: Any?,
|
||||
): BattleParams = BattleParams(
|
||||
map = Params.mapId,
|
||||
battleMode = 0,
|
||||
format = Params.formatMode,
|
||||
limits = Params.limits,
|
||||
mapId = Params.mapId,
|
||||
maxPeopleCount = Params.maxPeopleCount,
|
||||
name = Params.name?.toString() ?: "",
|
||||
parkourMode = Params.battleMode == "PARKOUR",
|
||||
valuableBattle = Params.battleOptions.contains("VALUABLE"),
|
||||
privateBattle = Params.battleOptions.contains("PRIVATE"),
|
||||
proBattle = Params.proBattle,
|
||||
rankRange = Params.rankRange,
|
||||
theme = Params.theme.toIntOrNull() ?: 0,
|
||||
suspicionLevel = SuspicionLevel,
|
||||
options = Params.battleOptions,
|
||||
)
|
||||
|
||||
/** 原 `eP`:队伍互换(字符串队伍名)。 */
|
||||
private fun Swap(Team: Any?): Any? = when (Team) {
|
||||
"RED", "red" -> "BLUE"
|
||||
"BLUE", "blue" -> "RED"
|
||||
else -> Team
|
||||
}
|
||||
|
||||
fun ReduceBattlesSelect(
|
||||
State0: BattlesSelectStateSlice,
|
||||
Action: Any,
|
||||
): BattlesSelectStateSlice = when (Action) {
|
||||
is RoundStarted -> State0.copy(
|
||||
RoundEndTimes = State0.RoundEndTimes + (Action.battleId to Action.timeLeftInSec),
|
||||
Score = State0.Score + (Action.battleId to emptyMap<Any?, Any?>()),
|
||||
)
|
||||
is SwapTeam -> {
|
||||
val Teams = Inner(State0.UsersTeam, Action.battleId)
|
||||
val Swapped = Teams.mapValues { (_, Team) -> Swap(Team) }
|
||||
val Users = Inner(State0.Users, Action.battleId)
|
||||
val Zeroed = State0.UserScore.mapValues { (UserId, _) ->
|
||||
if (Users.containsKey(UserId)) 0 else State0.UserScore[UserId]
|
||||
}
|
||||
State0.copy(
|
||||
UsersTeam = State0.UsersTeam + (Action.battleId to Swapped),
|
||||
UserScore = Zeroed,
|
||||
)
|
||||
}
|
||||
is RoundFinish -> State0.copy(
|
||||
RoundEndTimes = State0.RoundEndTimes - Action.battleId,
|
||||
Fund = State0.Fund - Action.battleId,
|
||||
)
|
||||
is MarkBattlesLoaded -> State0.copy(Loaded = true)
|
||||
is AddBattle -> State0.copy(
|
||||
Battles = State0.Battles + (
|
||||
Action.battleId to ParamsOf(Action.params, Action.suspicionLevel)
|
||||
),
|
||||
Users = State0.Users + (Action.battleId to emptyMap<Any?, Any?>()),
|
||||
Score = State0.Score + (Action.battleId to emptyMap<Any?, Any?>()),
|
||||
Creators = State0.Creators + (Action.battleId to Action.creatorId),
|
||||
)
|
||||
is RemoveBattle -> State0.copy(
|
||||
Battles = State0.Battles - Action.battleId,
|
||||
Users = State0.Users - Action.battleId,
|
||||
Creators = State0.Creators - Action.battleId,
|
||||
AlreadySeeBattleInfo = State0.AlreadySeeBattleInfo - Action.battleId,
|
||||
)
|
||||
is ChangeName -> {
|
||||
val Battle = State0.Battles[Action.battleId] as? BattleParams
|
||||
if (Battle == null) State0
|
||||
else State0.copy(
|
||||
Battles = State0.Battles + (
|
||||
Action.battleId to Battle.copy(name = Action.name?.toString() ?: Battle.name)
|
||||
),
|
||||
)
|
||||
}
|
||||
is UpdateSuspicion -> {
|
||||
val Battle = State0.Battles[Action.battleId] as? BattleParams
|
||||
if (Battle == null) State0
|
||||
else State0.copy(
|
||||
Battles = State0.Battles + (
|
||||
Action.battleId to Battle.copy(suspicionLevel = Action.suspicionLevel)
|
||||
),
|
||||
)
|
||||
}
|
||||
is AddUsers -> {
|
||||
var Users = State0.Users
|
||||
var Teams = State0.UsersTeam
|
||||
var Scores = State0.UserScore
|
||||
var Suspicion = State0.UserSuspicion
|
||||
var SuspicionByIp = State0.UserSuspicionByIp
|
||||
val CurrentUsers = LinkedHashMap<Any?, Any?>(Inner(State0.Users, Action.battleId))
|
||||
val CurrentTeams = LinkedHashMap<Any?, Any?>(Inner(State0.UsersTeam, Action.battleId))
|
||||
for (User in Action.users) {
|
||||
CurrentUsers[User.user] = User
|
||||
CurrentTeams[User.user] = Action.team
|
||||
Scores = Scores + (User.user to User.score)
|
||||
Suspicion = Suspicion + (User.user to User.suspicious)
|
||||
SuspicionByIp = SuspicionByIp + (User.user to User.suspiciousByIp)
|
||||
}
|
||||
Users = Users + (Action.battleId to CurrentUsers)
|
||||
Teams = Teams + (Action.battleId to CurrentTeams)
|
||||
State0.copy(
|
||||
Users = Users,
|
||||
UsersTeam = Teams,
|
||||
UserScore = Scores,
|
||||
UserSuspicion = Suspicion,
|
||||
UserSuspicionByIp = SuspicionByIp,
|
||||
)
|
||||
}
|
||||
is AddUser -> ReduceBattlesSelect(
|
||||
State0,
|
||||
AddUsers(
|
||||
battleId = Action.battleId,
|
||||
users = listOf(Action.infoUser as BattleInfoUser),
|
||||
team = Action.team,
|
||||
),
|
||||
)
|
||||
is RemoveUser -> State0.copy(
|
||||
Users = State0.Users + (
|
||||
Action.battleId to Inner(State0.Users, Action.battleId) - Action.userId
|
||||
),
|
||||
UsersTeam = State0.UsersTeam + (
|
||||
Action.battleId to Inner(State0.UsersTeam, Action.battleId) - Action.userId
|
||||
),
|
||||
UserScore = State0.UserScore - Action.userId,
|
||||
UserSuspicion = State0.UserSuspicion - Action.userId,
|
||||
UserSuspicionByIp = State0.UserSuspicionByIp - Action.userId,
|
||||
)
|
||||
is UpdateUserScore -> State0.copy(
|
||||
UserScore = State0.UserScore + (Action.userId to Action.score),
|
||||
)
|
||||
is UpdateUserSuspicion -> State0.copy(
|
||||
UserSuspicion = State0.UserSuspicion + (Action.userId to Action.suspicious),
|
||||
UserSuspicionByIp = State0.UserSuspicionByIp + (Action.userId to Action.suspiciousByIp),
|
||||
)
|
||||
is UpdateTeamScore -> {
|
||||
val Teams = LinkedHashMap<Any?, Any?>(Inner(State0.Score, Action.battleId))
|
||||
Teams[Action.team] = Action.score
|
||||
State0.copy(Score = State0.Score + (Action.battleId to Teams))
|
||||
}
|
||||
is ResetBattlesSelect -> BattlesSelectStateSlice()
|
||||
is EnterToBattleInfo ->
|
||||
State0.copy(AlreadySeeBattleInfo = State0.AlreadySeeBattleInfo + Action.battleId)
|
||||
is UpdateFund -> State0.copy(Fund = State0.Fund + (Action.battleId to Action.fund))
|
||||
is InitProBattleRewardLimit -> State0.copy(ProLimit = Action.proLimit)
|
||||
is LimitReached -> State0.copy(IsLimitReached = Action.isLimitReached)
|
||||
is SetReEnterProtectBattleId -> State0.copy(ReEnterProtectBattleId = Action.battleId)
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 80 个切片里,79 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 81 个切片里,80 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -82,9 +82,10 @@
|
||||
// battleChat → ReduceBattleChat(Q6 / Hf.wtg,根委派 `Hf.wtg(t, n.rpw_1)`)
|
||||
// remoteUserDataStorage → ReduceRemoteUserDataStorage($Q / vh.isl,根委派 `vh.isl(t, n.zpv_1)`)
|
||||
// matchmaking → ReduceMatchmaking(TH / JH().qrx,根委派 `JH().qrx(t, n.cpw_1)`)
|
||||
// battlesSelect → ReduceBattlesSelect(fA / Mi.vqk,根委派 `Mi.vqk(t, n.apw_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 9 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 8 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -97,6 +98,7 @@ import App.State.Reducers.ReduceBattleChat
|
||||
import App.State.Reducers.ReduceBattleMessage
|
||||
import App.State.Reducers.ReduceBattlePause
|
||||
import App.State.Reducers.ReduceBattleSearch
|
||||
import App.State.Reducers.ReduceBattlesSelect
|
||||
import App.State.Reducers.ReduceBattleSettings
|
||||
import App.State.Reducers.ReduceBattleStatistics
|
||||
import App.State.Reducers.ReduceBattleTutorial
|
||||
@@ -261,6 +263,7 @@ object RootReducer {
|
||||
BattleChat = ReduceBattleChat(State0.BattleChat, Action),
|
||||
RemoteUserDataStorage = ReduceRemoteUserDataStorage(State0.RemoteUserDataStorage, Action),
|
||||
Matchmaking = ReduceMatchmaking(State0.Matchmaking, Action),
|
||||
BattlesSelect = ReduceBattlesSelect(State0.BattlesSelect, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import App.State.Reducers.BattleSettingsStateSlice
|
||||
import App.State.Reducers.BattleStatisticsStateSlice
|
||||
import App.State.Reducers.BattleTutorialStateSlice
|
||||
import App.State.Reducers.BattleViewStateSlice
|
||||
import App.State.Reducers.BattlesSelectStateSlice
|
||||
import App.State.Reducers.CaptchaStateSlice
|
||||
import App.State.Reducers.ClanChatStateSlice
|
||||
import App.State.Reducers.ClanUserInfoStateSlice
|
||||
@@ -144,8 +145,8 @@ data class RootState(
|
||||
val Friends: FriendsStateSlice = FriendsStateSlice(),
|
||||
/** 原 remoteUserDataStorage($Q / vh.isl)。已迁移。 */
|
||||
val RemoteUserDataStorage: RemoteUserDataStorageStateSlice = RemoteUserDataStorageStateSlice(),
|
||||
/** 原 battlesSelect(fA / Mi.vqk)。未迁移:原样透传。 */
|
||||
val BattlesSelect: Any? = null,
|
||||
/** 原 battlesSelect(fA / Mi.vqk)。已迁移。 */
|
||||
val BattlesSelect: BattlesSelectStateSlice = BattlesSelectStateSlice(),
|
||||
/** 原 maps(OA / Oi.sql)。已迁移。 */
|
||||
val Maps: MapsStateSlice = MapsStateSlice(),
|
||||
/** 原 matchmaking(TH / JH().qrx)。已迁移。 */
|
||||
|
||||
@@ -42,7 +42,7 @@ object RootWiring {
|
||||
SliceWiring("notifications", "xpv_1", "jJ", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("AddNotification", "InitNotificationSound", "RemoveFirstNotification", "RemoveNotifications")),
|
||||
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "ClearContextMenuUser", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetContextMenuUser", "SetLimits", "SetValidationStatus")),
|
||||
SliceWiring("remoteUserDataStorage", "zpv_1", "\$Q", "vh.isl", "vh.isl(t, n.zpv_1)", listOf("UpdateClanTags", "UpdateOnlineStatus", "UpdatePremiumStatus", "UpdateRanks", "UpdateUids", "UserEnterBattle", "UserLeaveBattle", "UserLeaveGroup")),
|
||||
SliceWiring("battlesSelect", "apw_1", "fA", "Mi.vqk", "Mi.vqk(t, n.apw_1)", listOf("?dA", "?gA", "AddBattle", "AddUser", "AddUsers", "ChangeName", "EnterToBattleInfo", "InitProBattleRewardLimit", "LimitReached", "RemoveBattle", "RemoveUser", "RoundFinish", "RoundStarted", "SetReEnterProtectBattleId", "SwapTeam", "UpdateFund", "UpdateSuspicion", "UpdateTeamScore", "UpdateUserScore", "UpdateUserSuspicion")),
|
||||
SliceWiring("battlesSelect", "apw_1", "fA", "Mi.vqk", "Mi.vqk(t, n.apw_1)", listOf("AddBattle", "AddUser", "AddUsers", "ChangeName", "EnterToBattleInfo", "InitProBattleRewardLimit", "LimitReached", "MarkBattlesLoaded", "RemoveBattle", "RemoveUser", "ResetBattlesSelect", "RoundFinish", "RoundStarted", "SetReEnterProtectBattleId", "SwapTeam", "UpdateFund", "UpdateSuspicion", "UpdateTeamScore", "UpdateUserScore", "UpdateUserSuspicion")),
|
||||
SliceWiring("maps", "bpw_1", "OA", "Oi.sql", "Oi.sql(t, n.bpw_1)", listOf("AddMap")),
|
||||
SliceWiring("matchmaking", "cpw_1", "TH", "JH().qrx", "JH().qrx(t, n.cpw_1)", listOf("CancelLoadingBattle", "Configure", "EnterToBattleApply", "Play", "ResetMatchmakingProgress", "SelectMatchmakingMode", "SetRankIndexToMinGS", "StartLoadingBattle", "WasRegistered")),
|
||||
SliceWiring("battleCreateState", "dpw_1", "\$6", "Df.ttg", "Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)", listOf("?A6", "?B6", "?C6", "?D6", "?E6", "?F6", "?I6", "?L6", "?M6", "?N6", "?P6", "?R6", "?T6", "?U6", "Configure", "CreateBattle", "SetRestrictions")),
|
||||
@@ -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")
|
||||
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")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
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;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -767,6 +767,30 @@ import App.State.Reducers.MatchmakingStateSlice
|
||||
import App.State.Reducers.ReduceMatchmaking
|
||||
import App.State.Reducers.ResetMatchmakingProgress
|
||||
import App.State.Reducers.StartLoadingBattle
|
||||
import App.State.AddBattle
|
||||
import App.State.AddUser
|
||||
import App.State.AddUsers
|
||||
import App.State.BattleCreateParameters
|
||||
import App.State.BattleInfoUser
|
||||
import App.State.BattleParams
|
||||
import App.State.ChangeName
|
||||
import App.State.EnterToBattleInfo
|
||||
import App.State.InitProBattleRewardLimit
|
||||
import App.State.LimitReached
|
||||
import App.State.ProBattleRewardLimitData
|
||||
import App.State.RemoveUser
|
||||
import App.State.RoundFinish
|
||||
import App.State.RoundStarted
|
||||
import App.State.SetReEnterProtectBattleId
|
||||
import App.State.SwapTeam
|
||||
import App.State.UpdateSuspicion
|
||||
import App.State.UpdateTeamScore
|
||||
import App.State.UpdateUserScore
|
||||
import App.State.UpdateUserSuspicion
|
||||
import App.State.Reducers.BattlesSelectStateSlice
|
||||
import App.State.Reducers.MarkBattlesLoaded
|
||||
import App.State.Reducers.ReduceBattlesSelect
|
||||
import App.State.Reducers.ResetBattlesSelect
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1194,7 +1218,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 80 个已迁移切片(79 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 81 个已迁移切片(80 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1258,7 +1282,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 80 个已迁移字段,其余 9 个待迁",
|
||||
"根:迁移表 = 81 个已迁移字段,其余 8 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1282,9 +1306,9 @@ object StateCheck {
|
||||
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
||||
"remoteUserDataStorage", "matchmaking",
|
||||
"remoteUserDataStorage", "matchmaking", "battlesSelect",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 9
|
||||
RootWiring.Unmigrated.size == 8
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1340,7 +1364,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 80/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 81/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -6652,6 +6676,127 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 91 切片 reducer:BattlesSelect(fA / Mi.vqk,15 字段;20 个动作)
|
||||
run {
|
||||
fun Params(): BattleCreateParameters = BattleCreateParameters(
|
||||
battleMode = "DM", battleModeForMatchmaking = null,
|
||||
battleOptions = listOf("VALUABLE"), crossPlayMode = "", formatMode = "fmt",
|
||||
limits = "10", timeLimitInSec = "600", mapId = 1L, maxPeopleCount = 8,
|
||||
name = "b1", proBattle = true, rankRange = "1-5", min = "1", theme = "2",
|
||||
)
|
||||
|
||||
val S0 = BattlesSelectStateSlice()
|
||||
Check("切片 BattlesSelect:默认值完整", !S0.Loaded && S0.Battles.isEmpty())
|
||||
val S1 = ReduceBattlesSelect(S0, AddBattle(1L, 99L, Params(), "LOW"))
|
||||
val Battle = S1.Battles[1L] as BattleParams
|
||||
Check(
|
||||
"切片 BattlesSelect:AddBattle 建列表项与四个子表",
|
||||
Battle.name == "b1" && Battle.mapId == 1L && Battle.proBattle &&
|
||||
Battle.parkourMode == false && S1.Users[1L] == emptyMap<Any?, Any?>() &&
|
||||
S1.Score[1L] == emptyMap<Any?, Any?>() && S1.Creators[1L] == 99L
|
||||
)
|
||||
val S2 = ReduceBattlesSelect(S1, RoundStarted(1L, 60))
|
||||
Check(
|
||||
"切片 BattlesSelect:RoundStarted 写回合计时并清分数",
|
||||
S2.RoundEndTimes[1L] == 60 && S2.Score[1L] == emptyMap<Any?, Any?>()
|
||||
)
|
||||
val S3 = ReduceBattlesSelect(S2, UpdateTeamScore(1L, "RED", 3))
|
||||
Check(
|
||||
"切片 BattlesSelect:UpdateTeamScore 写队伍分",
|
||||
(S3.Score[1L] as Map<Any?, Any?>)["RED"] == 3
|
||||
)
|
||||
val S4 = ReduceBattlesSelect(
|
||||
S3,
|
||||
AddUsers(1L, listOf(BattleInfoUser(0L, 0, false, false, 7L)), "RED"),
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:AddUsers 写用户 / 队伍 / 个人分",
|
||||
(S4.Users[1L] as Map<Any?, Any?>).keys == setOf<Any?>(7L) &&
|
||||
(S4.UsersTeam[1L] as Map<Any?, Any?>)[7L] == "RED" &&
|
||||
S4.UserScore[7L] == 0
|
||||
)
|
||||
val S5 = ReduceBattlesSelect(
|
||||
ReduceBattlesSelect(S4, UpdateUserScore(1L, 7L, 9)),
|
||||
UpdateUserSuspicion(1L, 7L, true, false),
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:个人分与可疑标记",
|
||||
S5.UserScore[7L] == 9 && S5.UserSuspicion[7L] == true &&
|
||||
S5.UserSuspicionByIp[7L] == false
|
||||
)
|
||||
val S6 = ReduceBattlesSelect(S5, SwapTeam(1L))
|
||||
Check(
|
||||
"切片 BattlesSelect:SwapTeam 换队并把该用户分数归零",
|
||||
(S6.UsersTeam[1L] as Map<Any?, Any?>)[7L] == "BLUE" && S6.UserScore[7L] == 0
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:ChangeName / UpdateSuspicion",
|
||||
(ReduceBattlesSelect(S1, ChangeName(1L, "renamed"))
|
||||
.Battles[1L] as BattleParams).name == "renamed" &&
|
||||
(ReduceBattlesSelect(S1, UpdateSuspicion(1L, "HIGH"))
|
||||
.Battles[1L] as BattleParams).suspicionLevel == "HIGH"
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:EnterToBattleInfo / UpdateFund / LimitReached",
|
||||
ReduceBattlesSelect(S1, EnterToBattleInfo(1L))
|
||||
.AlreadySeeBattleInfo == setOf<Any?>(1L) &&
|
||||
ReduceBattlesSelect(S1, UpdateFund(1L, 20)).Fund[1L] == 20 &&
|
||||
ReduceBattlesSelect(S1, LimitReached(true)).IsLimitReached
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:InitProBattleRewardLimit / SetReEnterProtectBattleId",
|
||||
ReduceBattlesSelect(
|
||||
S1,
|
||||
InitProBattleRewardLimit(ProBattleRewardLimitData(1, 2, 3, 4)),
|
||||
).ProLimit == ProBattleRewardLimitData(1, 2, 3, 4) &&
|
||||
ReduceBattlesSelect(S1, SetReEnterProtectBattleId(5L))
|
||||
.ReEnterProtectBattleId == 5L
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:RemoveUser 清四个个人表",
|
||||
ReduceBattlesSelect(S5, RemoveUser(1L, 7L)).let {
|
||||
(it.Users[1L] as Map<Any?, Any?>).isEmpty() &&
|
||||
it.UserScore[7L] == null && it.UserSuspicion[7L] == null
|
||||
}
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:RemoveBattle 清四个战斗表",
|
||||
ReduceBattlesSelect(
|
||||
ReduceBattlesSelect(S5, EnterToBattleInfo(1L)),
|
||||
RemoveBattle(1L),
|
||||
).let {
|
||||
it.Battles.isEmpty() && it.Users.isEmpty() && it.Creators.isEmpty() &&
|
||||
it.AlreadySeeBattleInfo.isEmpty()
|
||||
}
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:RoundFinish 删计时与资金",
|
||||
ReduceBattlesSelect(ReduceBattlesSelect(S2, UpdateFund(1L, 5)), RoundFinish(1L))
|
||||
.let { it.RoundEndTimes[1L] == null && it.Fund[1L] == null }
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:MarkBattlesLoaded / ResetBattlesSelect",
|
||||
ReduceBattlesSelect(S0, MarkBattlesLoaded).Loaded &&
|
||||
ReduceBattlesSelect(S5, ResetBattlesSelect) == BattlesSelectStateSlice()
|
||||
)
|
||||
Check(
|
||||
"切片 BattlesSelect:无关动作不改状态",
|
||||
ReduceBattlesSelect(S5, AddFrags(1)) == S5
|
||||
)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, MarkBattlesLoaded)
|
||||
Check(
|
||||
"根:MarkBattlesLoaded 只动 battlesSelect",
|
||||
Root1.BattlesSelect.Loaded && Root1.Matchmaking == Root0.Matchmaking
|
||||
)
|
||||
val W = RootWiring.ByField("battlesSelect")!!
|
||||
Check(
|
||||
"根:battlesSelect 委派到 fA / Mi.vqk 且 20 个动作",
|
||||
W.SliceClass == "fA" && W.Reducer == "Mi.vqk" && W.Actions.size == 20
|
||||
)
|
||||
}
|
||||
|
||||
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