feat(state): 迁移 maps 并补地图描述协议模型
This commit is contained in:
1 parent
9cf760b354
commit
8dac8d96e8
8 files changed
+164
-19
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` 动作名。
|
||||
- 已迁移切片 **76/89**:
|
||||
- 已迁移切片 **77/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)
|
||||
@@ -384,12 +384,16 @@
|
||||
`LocalizedMessage(…, content, arguments, isHidden)`、`LocalizedUserMessage(…, content, userTeamRelation, userUid)`、
|
||||
`LocalizedTeamMessage(…, content, teamRelation)`;`HideMessage` 只把 `hideTimeMs` 匹配的消息标隐藏,
|
||||
`RemoveMessage` 删掉匹配项,`sT`→`ResetBattleMessages` 整片复位。
|
||||
- `maps` → `OA` / `Oi.sql`(`MapsReducer.kt`):1 字段,1 个分支。
|
||||
`AddMap` 的动作载荷就是生成物里的 `MapInfoCC`(11 个字段,与源码 properties 一致),
|
||||
直接复用;reducer 以 `info.mapId` 为键新建 / 复用 `MapsDescriptionEntry`,
|
||||
再把 `MapsThemeEntry` 写进 `themes[info.theme]`(同地图多主题是合并)。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 75 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **13 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 76/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **832 项全绿**,含 89 槽位完整性、75 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 76 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **12 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 77/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **838 项全绿**,含 89 槽位完整性、76 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T11:41:22.721Z",
|
||||
"generatedAt": "2026-10-24T11:49:38.715Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -82,7 +82,8 @@
|
||||
"battleSettings",
|
||||
"contractState",
|
||||
"tankBuilderState",
|
||||
"battleMessage"
|
||||
"battleMessage",
|
||||
"maps"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -1381,6 +1382,11 @@
|
||||
"Name": "ResetBattleMessages",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "new FE() 整片复位"
|
||||
},
|
||||
{
|
||||
"Local": "FA",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "按 info.mapId 新建/复用 MapDescription,并把 MapInfo 写进 themes[themeId](原名 AddMap)"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -2139,7 +2145,7 @@
|
||||
"AddMap"
|
||||
],
|
||||
"Default": "new OA()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "matchmaking",
|
||||
|
||||
@@ -795,6 +795,7 @@ const Migrated = {
|
||||
contractState: { Type: 'ContractStateSlice', Reduce: 'ReduceContractState' },
|
||||
tankBuilderState: { Type: 'TankBuilderStateSlice', Reduce: 'ReduceTankBuilder' },
|
||||
battleMessage: { Type: 'BattleMessageStateSlice', Reduce: 'ReduceBattleMessage' },
|
||||
maps: { Type: 'MapsStateSlice', Reduce: 'ReduceMaps' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -1202,6 +1203,7 @@ const AppSettingsAnonymousEffects = {
|
||||
Hnt: '只保留 rewards 非空的合同',
|
||||
Trt: 'new brt() 整片复位',
|
||||
sT: 'new FE() 整片复位',
|
||||
FA: '按 info.mapId 新建/复用 MapDescription,并把 MapInfo 写进 themes[themeId](原名 AddMap)',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// MapsReducer.kt —— 地图描述切片(对应 module_10032 的 OA 状态 + GA.sql)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 OA:1 个字段,toString 名 "Maps(mapDescriptionsById=…)",缺省 emptyMap()。
|
||||
// 根委派:`Oi.sql(t, n.bpw_1)`。
|
||||
// 动作 `FA`(AddMap)的 toString 名 "AddMap(id=…, info=…)",载荷类型就是生成物里的 `MapInfoCC`
|
||||
// (`defaultTheme / enabled / mapId / mapName / matchmakingMark / maxPeople / preview /
|
||||
// rankLimit / min / supportedModes / theme`,与源码 `properties` 一一对应)。
|
||||
// reducer:
|
||||
// FA → 以 `info.mapId` 为键:已有 `LA`(MapDescription)就复用,否则新建
|
||||
// `MapDescription(mapId, mapName, defaultTheme, emptyMap())`;
|
||||
// 再把本次主题写进 `themes[info.theme]`(`DA` = MapInfo)。
|
||||
//
|
||||
// 生成物里另有同名的 `MapDescription` / `MapInfo`(字段是 Int 版),为避免歧义,
|
||||
// 这里用 `MapsDescriptionEntry` / `MapsThemeEntry` 两个包内模型。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.AddMap
|
||||
|
||||
/* ------------------------------------------------------------------ 模型 */
|
||||
|
||||
data class MapsThemeEntry(
|
||||
val Theme: String,
|
||||
val MapName: Any?,
|
||||
val Enabled: Boolean,
|
||||
val DefaultTheme: String,
|
||||
val MatchmakingMark: Boolean,
|
||||
val MaxPeople: Int,
|
||||
val Preview: Any?,
|
||||
val RankLimit: String,
|
||||
val Min: String,
|
||||
val SupportedModes: List<String> = emptyList(),
|
||||
)
|
||||
|
||||
data class MapsDescriptionEntry(
|
||||
val MapId: Long,
|
||||
val Name: Any?,
|
||||
val DefaultTheme: String,
|
||||
val Themes: Map<Any?, Any?> = emptyMap(),
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class MapsStateSlice(
|
||||
val MapDescriptionsById: Map<Any?, Any?> = emptyMap(),
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceMaps(
|
||||
State0: MapsStateSlice,
|
||||
Action: Any,
|
||||
): MapsStateSlice = when (Action) {
|
||||
is AddMap -> {
|
||||
val Info = Action.info
|
||||
val Existing = State0.MapDescriptionsById[Info.mapId] as? MapsDescriptionEntry
|
||||
val Base = Existing ?: MapsDescriptionEntry(
|
||||
MapId = Info.mapId,
|
||||
Name = Info.mapName,
|
||||
DefaultTheme = Info.defaultTheme,
|
||||
)
|
||||
val Entry = MapsThemeEntry(
|
||||
Theme = Info.theme,
|
||||
MapName = Info.mapName,
|
||||
Enabled = Info.enabled,
|
||||
DefaultTheme = Info.defaultTheme,
|
||||
MatchmakingMark = Info.matchmakingMark,
|
||||
MaxPeople = Info.maxPeople,
|
||||
Preview = Info.preview,
|
||||
RankLimit = Info.rankLimit,
|
||||
Min = Info.min,
|
||||
SupportedModes = Info.supportedModes,
|
||||
)
|
||||
State0.copy(
|
||||
MapDescriptionsById = State0.MapDescriptionsById + (
|
||||
Info.mapId to Base.copy(Themes = Base.Themes + (Info.theme to Entry))
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 76 个切片里,75 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 77 个切片里,76 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -78,9 +78,10 @@
|
||||
// contractState → ReduceContractState(Ont / xv.bti,根委派 `xv.bti(t, n.fpy_1)`)
|
||||
// tankBuilderState → ReduceTankBuilder(brt / wp.ati,根委派 `wp.ati(t, n.epy_1)`)
|
||||
// battleMessage → ReduceBattleMessage(FE / vT().rqb,根委派 `vT().rqb(t, n.qpw_1)`)
|
||||
// maps → ReduceMaps(OA / Oi.sql,根委派 `Oi.sql(t, n.bpw_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 13 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 12 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -121,6 +122,7 @@ import App.State.Reducers.ReduceLocalBattleUser
|
||||
import App.State.Reducers.ReduceLoginByPassword
|
||||
import App.State.Reducers.ReduceLootBoxes
|
||||
import App.State.Reducers.ReduceMainQuest
|
||||
import App.State.Reducers.ReduceMaps
|
||||
import App.State.Reducers.ReduceAvailabilityByRank
|
||||
import App.State.Reducers.ReduceLobbyLoader
|
||||
import App.State.Reducers.ReduceMigrationState
|
||||
@@ -249,6 +251,7 @@ object RootReducer {
|
||||
ContractState = ReduceContractState(State0.ContractState, Action),
|
||||
TankBuilderState = ReduceTankBuilder(State0.TankBuilderState, Action),
|
||||
BattleMessage = ReduceBattleMessage(State0.BattleMessage, Action),
|
||||
Maps = ReduceMaps(State0.Maps, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import App.State.Reducers.LocalBattleUserStateSlice
|
||||
import App.State.Reducers.LoginByPasswordStateSlice
|
||||
import App.State.Reducers.LootBoxesStateSlice
|
||||
import App.State.Reducers.MainQuestStateSlice
|
||||
import App.State.Reducers.MapsStateSlice
|
||||
import App.State.Reducers.MigrationStateSlice
|
||||
import App.State.Reducers.ModalScreenStateSlice
|
||||
import App.State.Reducers.ModerationStateSlice
|
||||
@@ -142,8 +143,8 @@ data class RootState(
|
||||
val RemoteUserDataStorage: Any? = null,
|
||||
/** 原 battlesSelect(fA / Mi.vqk)。未迁移:原样透传。 */
|
||||
val BattlesSelect: Any? = null,
|
||||
/** 原 maps(OA / Oi.sql)。未迁移:原样透传。 */
|
||||
val Maps: Any? = null,
|
||||
/** 原 maps(OA / Oi.sql)。已迁移。 */
|
||||
val Maps: MapsStateSlice = MapsStateSlice(),
|
||||
/** 原 matchmaking(TH / JH().qrx)。未迁移:原样透传。 */
|
||||
val Matchmaking: Any? = null,
|
||||
/** 原 battleCreateState($6 / Df.ttg)。未迁移:原样透传。 */
|
||||
|
||||
@@ -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")
|
||||
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")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
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;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -730,6 +730,12 @@ import App.State.Reducers.LocalizedUserMessage
|
||||
import App.State.Reducers.ReduceBattleMessage
|
||||
import App.State.Reducers.ResetBattleMessages
|
||||
import App.State.Reducers.TextMessage
|
||||
import App.State.AddMap
|
||||
import App.State.MapInfoCC
|
||||
import App.State.Reducers.MapsDescriptionEntry
|
||||
import App.State.Reducers.MapsStateSlice
|
||||
import App.State.Reducers.MapsThemeEntry
|
||||
import App.State.Reducers.ReduceMaps
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1157,7 +1163,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 76 个已迁移切片(75 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 77 个已迁移切片(76 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1221,7 +1227,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 76 个已迁移字段,其余 13 个待迁",
|
||||
"根:迁移表 = 77 个已迁移字段,其余 12 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1244,9 +1250,9 @@ object StateCheck {
|
||||
"clanChatState", "battleSearchState", "battleStatistics",
|
||||
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||
"tankBuilderState", "battleMessage",
|
||||
"tankBuilderState", "battleMessage", "maps",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 13
|
||||
RootWiring.Unmigrated.size == 12
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1302,7 +1308,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 76/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 77/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -6349,6 +6355,48 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 87 切片 reducer:Maps(OA / Oi.sql,1 字段;AddMap)
|
||||
run {
|
||||
fun Info(Theme: String): MapInfoCC = MapInfoCC(
|
||||
defaultTheme = "summer", enabled = true, mapId = 1L, mapName = "Desert",
|
||||
matchmakingMark = true, maxPeople = 16, preview = null, rankLimit = "1-5",
|
||||
min = "1", supportedModes = listOf("DM"), theme = Theme,
|
||||
)
|
||||
|
||||
val S0 = MapsStateSlice()
|
||||
Check("切片 Maps:默认空表", S0.MapDescriptionsById.isEmpty())
|
||||
val S1 = ReduceMaps(S0, AddMap(0L, Info("summer")))
|
||||
val Desc1 = S1.MapDescriptionsById[1L] as MapsDescriptionEntry
|
||||
Check(
|
||||
"切片 Maps:AddMap 新建地图描述并写入模式条目",
|
||||
Desc1.Name == "Desert" && Desc1.DefaultTheme == "summer" &&
|
||||
Desc1.Themes.keys == setOf<Any?>("summer") &&
|
||||
(Desc1.Themes["summer"] as MapsThemeEntry).MapName == "Desert"
|
||||
)
|
||||
val S2 = ReduceMaps(S1, AddMap(1L, Info("winter")))
|
||||
val Desc2 = S2.MapDescriptionsById[1L] as MapsDescriptionEntry
|
||||
Check(
|
||||
"切片 Maps:同地图新增主题是合并而不是覆盖",
|
||||
Desc2.Themes.keys == setOf<Any?>("summer", "winter") &&
|
||||
(Desc2.Themes["winter"] as MapsThemeEntry).Theme == "winter"
|
||||
)
|
||||
Check("切片 Maps:无关动作不改状态", ReduceMaps(S2, AddFrags(1)) == S2)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, AddMap(0L, Info("summer")))
|
||||
Check(
|
||||
"根:AddMap 只动 maps",
|
||||
Root1.Maps.MapDescriptionsById.keys == setOf<Any?>(1L) &&
|
||||
Root1.BattleMessage == Root0.BattleMessage
|
||||
)
|
||||
val W = RootWiring.ByField("maps")!!
|
||||
Check(
|
||||
"根:maps 委派到 OA / Oi.sql",
|
||||
W.SliceClass == "OA" && W.Reducer == "Oi.sql" &&
|
||||
W.Actions == listOf("AddMap")
|
||||
)
|
||||
}
|
||||
|
||||
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