fix(session): wire 模型经适配器转为状态模型,跳过同名类型冲突
This commit is contained in:
1 parent
ee7504000f
commit
1d1479592f
7 files changed
+252
-45
No files matched your search
@@ -519,6 +519,24 @@
|
||||
- 下一步:用户拥有的物品列表来自 GarageCC 的引用字段(`itemsOnDepot` / `itemsOnMarket` /
|
||||
`mountedItems`)与模型命令流;等这部分抓包/命令解码补齐后,用目录把 id 解析成
|
||||
`GarageItemObject` 再派发 `SetItemsOnDepot` / `SetItemsOnMarket` / `SetMountedItems`。
|
||||
- wire → 状态层模型适配(`App/Session/WireAdapters.kt`):
|
||||
- 修正了一个会静默失败的问题:`DownlinkActions` 之前直接 `Value as UserPropertiesCC`,
|
||||
但 codec 解出的是 `Net.Generated.UserPropertiesCC`(字段 `Field1…`),
|
||||
动作要的是 `App.State.UserPropertiesCC`(语义字段),运行时必然 ClassCastException
|
||||
(会话自检里的「失败 3」就是这类转换)。
|
||||
- 生成器 `gen_downlink_actions.js` 增加 `Adapters` 表:需要转换的模型改为调用
|
||||
`WireAdapters.ToXxx(Value as Net.Generated.Xxx)`;当前覆盖 3 个模型
|
||||
(UserProperties / ReferralEventUserModel / GaragePreview)。
|
||||
- `UserPropertiesCC` 的 wire 字段 ↔ 语义名取自 module_47243 的 `Gs.t7h`(写入顺序)
|
||||
与 `fi(lg).toString`(槽位 → 名称),逐位对应关系登记在适配器文件头;
|
||||
`rankBounds` 的嵌套对象拆成状态层的 `rankBounds` / `topBound` 两个字符串。
|
||||
- 会话自检新增合成用例:构造一条 `UserPropertiesCC` Pt 记录,断言
|
||||
`Uid / Id / Crystals / Rank / Score / HasSpectatorPermissions` 写进 `RootState.User`。
|
||||
- 同名类型冲突:`ProtocolMetadata.Downlink` 里部分方法的实际 Kotlin 类型带后缀
|
||||
(`GaragePreviewModelCC0004`、`UserPropertiesCC5452` 等,跨切片同名类改名)。
|
||||
生成器现在读取该表,跳过「实际类型 ≠ 语义模型名」的方法(4 条),
|
||||
不再生成必然 ClassCastException 的映射;清单记在 `downlink_actions.json` 的
|
||||
`SkippedTypeConflicts`。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
`launchParams` 是纯透传(`(pass-through)`,无 reducer),已把字段收口为 `Map<String, String>`。
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"Methods": 681,
|
||||
"Resolved": 477,
|
||||
"Matched": 13,
|
||||
"Matched": 9,
|
||||
"MatchedModels": [
|
||||
{
|
||||
"Model": "GaragePreviewModelCC",
|
||||
@@ -26,6 +26,28 @@
|
||||
"Field": "user"
|
||||
}
|
||||
],
|
||||
"SkippedTypeConflicts": [
|
||||
{
|
||||
"MethodId": "8836555743203700004",
|
||||
"Model": "GaragePreviewModelCC",
|
||||
"Type": "GaragePreviewModelCC0004"
|
||||
},
|
||||
{
|
||||
"MethodId": "2940608322101861340",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Type": "ReferralEventUserModelCC1340"
|
||||
},
|
||||
{
|
||||
"MethodId": "5628691596950015452",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Type": "UserPropertiesCC5452"
|
||||
},
|
||||
{
|
||||
"MethodId": "7864034006236084309",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Type": "UserPropertiesCC4309"
|
||||
}
|
||||
],
|
||||
"Models": [
|
||||
{
|
||||
"Model": "AndroidPayCC",
|
||||
@@ -830,24 +852,12 @@
|
||||
}
|
||||
],
|
||||
"Actions": [
|
||||
{
|
||||
"MethodId": "8836555743203700004",
|
||||
"Model": "GaragePreviewModelCC",
|
||||
"Action": "InitGarageResources",
|
||||
"Field": "params"
|
||||
},
|
||||
{
|
||||
"MethodId": "2940606982196178690",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"MethodId": "2940608322101861340",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
"Action": "SetReferralEventData",
|
||||
"Field": "eventData"
|
||||
},
|
||||
{
|
||||
"MethodId": "1880027114297758713",
|
||||
"Model": "ReferralEventUserModelCC",
|
||||
@@ -878,18 +888,6 @@
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "5628691596950015452",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "7864034006236084309",
|
||||
"Model": "UserPropertiesCC",
|
||||
"Action": "SetUserPropertiesAction",
|
||||
"Field": "user"
|
||||
},
|
||||
{
|
||||
"MethodId": "3977381235093458189",
|
||||
"Model": "UserPropertiesCC",
|
||||
|
||||
@@ -25,6 +25,22 @@ const MethodTable = JSON.parse(fs.readFileSync(path.join(Names, 'method_table.js
|
||||
const CodecFields = JSON.parse(fs.readFileSync(path.join(Names, 'codec_fields.json'), 'utf8')).Codecs;
|
||||
const ModelBinding = JSON.parse(fs.readFileSync(path.join(Names, 'model_binding.json'), 'utf8')).Entries;
|
||||
const Inventory = fs.readFileSync(path.join(KotlinRoot, 'Inventory.kt'), 'utf8');
|
||||
const MetadataPath = path.resolve(
|
||||
P, '..', 'TankiOffline', 'net', 'src', 'jsMain', 'kotlin', 'Net', 'Generated', 'Metadata.kt',
|
||||
);
|
||||
|
||||
// 方法 → 生成代码里实际使用的 Kotlin 类型名。
|
||||
// 同名协议模型跨切片冲突时,生成器会加后缀(如 GaragePreviewModelCC0004);
|
||||
// 这类方法不能按语义名去匹配动作,否则运行时 ClassCastException。
|
||||
const MethodType = new Map();
|
||||
try {
|
||||
const Meta = fs.readFileSync(MetadataPath, 'utf8');
|
||||
const Re = /(\d+)L to ProtocolCommandMeta\(\d+L, "([^"]+)"/g;
|
||||
let M;
|
||||
while ((M = Re.exec(Meta))) MethodType.set(M[1], M[2]);
|
||||
} catch (E) {
|
||||
// 没有生成产物时退化为「不检查类型冲突」,保持旧行为。
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 模型名解析
|
||||
const ByModuleCodec = new Map();
|
||||
@@ -62,6 +78,21 @@ function ParseActions(Text) {
|
||||
const Actions = ParseActions(Inventory);
|
||||
const ModelNames = new Set(Rows.map((R) => R.Model).filter(Boolean));
|
||||
|
||||
// 模型名 → 候选动作(字段类型恰为该模型,且动作只有这一个字段)
|
||||
// 注意:wire 模型(Net.Generated)与状态层模型(App.State)同名但类型不同,
|
||||
// 这些模型必须经 App/Session/WireAdapters.kt 显式转换,不能直接 `as`。
|
||||
const Adapters = {
|
||||
UserPropertiesCC: 'App.Session.WireAdapters.ToUserProperties',
|
||||
ReferralEventUserModelCC: 'App.Session.WireAdapters.ToReferralEvent',
|
||||
GaragePreviewModelCC: 'App.Session.WireAdapters.ToGaragePreview',
|
||||
};
|
||||
|
||||
function ValueExpr(Model, TypeName) {
|
||||
return Adapters[Model]
|
||||
? `${Adapters[Model]}(Value as Net.Generated.${TypeName})`
|
||||
: `Value as ${TypeName}`;
|
||||
}
|
||||
|
||||
// 模型名 → 候选动作(字段类型恰为该模型,且动作只有这一个字段)
|
||||
const ActionByModel = new Map();
|
||||
for (const [Name, Fields] of Actions) {
|
||||
@@ -75,6 +106,7 @@ for (const [Name, Fields] of Actions) {
|
||||
// ---------------------------------------------------------------- 汇总
|
||||
const Matched = [];
|
||||
const MatchedModels = [];
|
||||
const SkippedTypeConflicts = [];
|
||||
const Models = [];
|
||||
for (const Name of [...ModelNames].sort()) {
|
||||
const Cand = ActionByModel.get(Name) || [];
|
||||
@@ -82,6 +114,11 @@ for (const Name of [...ModelNames].sort()) {
|
||||
Models.push({ Model: Name, Methods: Methods.length, Actions: Cand.map((C) => C.Action) });
|
||||
if (Cand.length === 1) {
|
||||
for (const Id of Methods) {
|
||||
const ActualType = MethodType.get(String(Id));
|
||||
if (ActualType && ActualType !== Name) {
|
||||
SkippedTypeConflicts.push({ MethodId: Id, Model: Name, Type: ActualType });
|
||||
continue;
|
||||
}
|
||||
Matched.push({ MethodId: Id, Model: Name, Action: Cand[0].Action, Field: Cand[0].Field });
|
||||
}
|
||||
MatchedModels.push({ Model: Name, Action: Cand[0].Action, Field: Cand[0].Field });
|
||||
@@ -101,6 +138,7 @@ fs.writeFileSync(
|
||||
Resolved: Rows.filter((R) => R.Model).length,
|
||||
Matched: Matched.length,
|
||||
MatchedModels,
|
||||
SkippedTypeConflicts,
|
||||
Models,
|
||||
Actions: Matched,
|
||||
},
|
||||
@@ -121,13 +159,16 @@ Lines.push('// → Inventory.kt 里唯一一个「只带该模型字段」的
|
||||
Lines.push('//');
|
||||
Lines.push('// 覆盖范围:仅自动可判定的组合;其余协议模型需要 CC → 多字段的转换动作,');
|
||||
Lines.push('// 见 names/downlink_actions.json 的 Models 清单(后续按协议逐个补转换器)。');
|
||||
Lines.push('//');
|
||||
Lines.push('// wire 模型(Net.Generated)与状态层模型(App.State)同名但类型不同;');
|
||||
Lines.push('// 需要转换的模型走 App/Session/WireAdapters.kt,生成器里登记在 Adapters 表。');
|
||||
Lines.push('package App.State');
|
||||
Lines.push('');
|
||||
Lines.push('/** 每条记录解码后,用 (Any) -> Any? 构造对应动作;未覆盖的 methodId 不在表里。 */');
|
||||
Lines.push('val DownlinkActions: Map<Long, (Any) -> Any?> = mapOf(');
|
||||
for (const M of Matched) {
|
||||
Lines.push(
|
||||
' ' + M.MethodId + 'L to { Value -> ' + M.Action + '(Value as ' + M.Model + ') },',
|
||||
' ' + M.MethodId + 'L to { Value -> ' + M.Action + '(' + ValueExpr(M.Model, M.Model) + ') },',
|
||||
);
|
||||
}
|
||||
Lines.push(')');
|
||||
@@ -140,7 +181,9 @@ for (const M of MatchedModels) {
|
||||
return ByModuleCodec.get(Key) === M.Model;
|
||||
}).map((E) => E.ModelId);
|
||||
for (const Id of Ids) {
|
||||
Lines.push(' ' + Id + 'L to { Value -> ' + M.Action + '(Value as ' + M.Model + ') },');
|
||||
Lines.push(
|
||||
' ' + Id + 'L to { Value -> ' + M.Action + '(' + ValueExpr(M.Model, M.Model) + ') },',
|
||||
);
|
||||
}
|
||||
}
|
||||
Lines.push(')');
|
||||
@@ -149,5 +192,6 @@ fs.writeFileSync(path.join(KotlinRoot, 'DownlinkActions.kt'), Lines.join('\n'),
|
||||
|
||||
console.log(
|
||||
`methodId ${Rows.length} 个,解析出协议模型 ${Rows.filter((R) => R.Model).length} 个;` +
|
||||
`自动匹配动作 ${Matched.length} 条,涉及模型 ${new Set(Matched.map((M) => M.Model)).size} 个。`,
|
||||
`自动匹配动作 ${Matched.length} 条,涉及模型 ${new Set(Matched.map((M) => M.Model)).size} 个;` +
|
||||
`跳过同名类型冲突 ${SkippedTypeConflicts.length} 条。`,
|
||||
);
|
||||
@@ -115,6 +115,46 @@ object SessionCheck {
|
||||
Router.RouteAll(List)
|
||||
}
|
||||
|
||||
// 合成一条 UserPropertiesCC 容器记录:验证 wire 模型 → 动作 → reducer 的闭环。
|
||||
// 抓包里没有用户属性帧,所以这里只用固定的 wire 值做通路自检(不依赖抓包)。
|
||||
val Probe = SessionRouter()
|
||||
val UserWire = Net.Generated.UserPropertiesCC(
|
||||
Field1 = 11,
|
||||
CanUseGroup = true,
|
||||
Field3 = 500,
|
||||
Field4 = 7,
|
||||
Field5 = 22,
|
||||
Field6 = 0L,
|
||||
Field7 = 33,
|
||||
Field8 = 9,
|
||||
HasSpectatorPermissions = true,
|
||||
Field10 = 44,
|
||||
Field11 = 777001L,
|
||||
PartnerUid = null,
|
||||
Field13 = 5,
|
||||
RankBounds = Net.Generated.RankBounds(4, 9),
|
||||
Field15 = 1,
|
||||
Field16 = 55,
|
||||
Field17 = 66,
|
||||
Field18 = 77,
|
||||
UID = "uid-check",
|
||||
UserProfileUrl = "https://example/",
|
||||
Field21 = 88,
|
||||
Field22 = 99,
|
||||
)
|
||||
val UserPt = Net.Protocol.PtPayload(
|
||||
listOf(Net.Protocol.PtObject(777001L, 0L)),
|
||||
listOf(Net.Protocol.PtItem(1893408944113965505L, UserWire)),
|
||||
)
|
||||
Probe.Route(Net.Protocol.DecodedRecord(777001L, 0L, UserPt, ByteArray(0), ByteArray(0)))
|
||||
val U = Probe.Store.State.User
|
||||
val UserOk = U.Uid == "uid-check" && U.Id == 777001L && U.Crystals == 500 &&
|
||||
U.Rank == 5 && U.Score == 66 && U.HasSpectatorPermissions && Probe.Applied == 1
|
||||
println(
|
||||
"用户属性闭环:Uid=${U.Uid}|Id=${U.Id}|Crystals=${U.Crystals}|Rank=${U.Rank}" +
|
||||
"|Score=${U.Score}|应用=${Probe.Applied}|${if (UserOk) "通过" else "失败"}"
|
||||
)
|
||||
|
||||
println("文件 $Frames|记录 $Records|回编码一致 $Ok|不一致 $Bad|记录头截断 $HeaderErrors|其它错误 $Errors|别名表=${Session.AliasCount}")
|
||||
println("Pt 容器 $PtRecords 条|项 $PtItems|单条最多 $PtMaxItems")
|
||||
for (S in PtSample) println(" pt 样本:$S")
|
||||
@@ -149,12 +189,25 @@ object SessionCheck {
|
||||
if (TopUnmapped.isNotEmpty()) {
|
||||
println("未覆盖 id:" + TopUnmapped.joinToString(",") { "${it.key}×${it.value}" })
|
||||
}
|
||||
if (Router.FailedIds.isNotEmpty()) {
|
||||
val TopFailed = Router.FailedIds.entries.sortedByDescending { it.value }.take(6)
|
||||
println(
|
||||
"失败 id:" + TopFailed.joinToString(",") {
|
||||
val Name = App.State.ModelNamesByModelId[it.key]
|
||||
(if (Name != null) "$Name(${it.key})" else it.key.toString()) + "×" + it.value
|
||||
}
|
||||
)
|
||||
}
|
||||
if (Router.FailedReasons.isNotEmpty()) {
|
||||
val TopReason = Router.FailedReasons.entries.sortedByDescending { it.value }.take(2)
|
||||
println("失败原因:" + TopReason.joinToString(";") { "${it.key}×${it.value}" })
|
||||
}
|
||||
val Top = Methods.entries.sortedByDescending { it.value }.take(5)
|
||||
if (Top.isNotEmpty()) {
|
||||
println("高频 methodId:" + Top.joinToString(",") { "${it.key}×${it.value}" })
|
||||
}
|
||||
// 抓包目录里混有入口/握手帧,解不出记录是正常现象;只要所有解出的记录都能回编码即可。
|
||||
val Passed = Bad == 0 && Records > 0 && Router.Catalog.Items.isNotEmpty()
|
||||
val Passed = Bad == 0 && Records > 0 && Router.Catalog.Items.isNotEmpty() && UserOk
|
||||
println(if (Passed) "结果:通过" else "结果:失败")
|
||||
ExitSession(if (Passed) 0 else 1)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import App.State.RootState
|
||||
import Net.Protocol.DecodedRecord
|
||||
import Net.Protocol.PtPayload
|
||||
import State.ReduxStore
|
||||
import kotlin.js.js
|
||||
|
||||
class SessionRouter(InitialState: RootState = RootState()) {
|
||||
|
||||
@@ -35,6 +36,15 @@ class SessionRouter(InitialState: RootState = RootState()) {
|
||||
/** 未覆盖的协议模型名计数(model_registry 能解析出名字的部分)。 */
|
||||
val UnmappedModels: MutableMap<String, Int> = LinkedHashMap()
|
||||
|
||||
/** 转换/派发失败的 id 计数(methodId 或 Pt item modelId)。 */
|
||||
val FailedIds: MutableMap<Long, Int> = LinkedHashMap()
|
||||
|
||||
/** 失败原因计数(异常信息),便于定位是类型不匹配还是字段缺失。 */
|
||||
val FailedReasons: MutableMap<String, Int> = LinkedHashMap()
|
||||
|
||||
private fun RuntimeName(V: Any?): String =
|
||||
js("(function (V) { return V && V.constructor ? V.constructor.name : '?' })(V)") as String
|
||||
|
||||
/** 已应用动作的类名(调试 / 自检用)。 */
|
||||
val AppliedNames: MutableList<String> = ArrayList()
|
||||
|
||||
@@ -69,6 +79,9 @@ class SessionRouter(InitialState: RootState = RootState()) {
|
||||
}
|
||||
} catch (E: Throwable) {
|
||||
Failed++
|
||||
FailedIds[Item.Id] = (FailedIds[Item.Id] ?: 0) + 1
|
||||
val Why = (E.message ?: E.toString()) + " @ " + RuntimeName(Item.Value)
|
||||
FailedReasons[Why] = (FailedReasons[Why] ?: 0) + 1
|
||||
}
|
||||
}
|
||||
return Last
|
||||
@@ -89,6 +102,9 @@ class SessionRouter(InitialState: RootState = RootState()) {
|
||||
Action
|
||||
} catch (E: Throwable) {
|
||||
Failed++
|
||||
FailedIds[Record.MethodId] = (FailedIds[Record.MethodId] ?: 0) + 1
|
||||
val Why = E.message ?: E.toString()
|
||||
FailedReasons[Why] = (FailedReasons[Why] ?: 0) + 1
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// WireAdapters.kt —— 线上协议模型(Net.Generated)→ 状态层动作载荷(App.State)
|
||||
//
|
||||
// 为什么需要这一层:
|
||||
// codec 解出来的是 `Net.Generated.*`(按字段表生成的 wire 模型,匿名字段叫 Field1…),
|
||||
// 而动作 / reducer 期望的是 `App.State.*`(按原包 toString / 调用点还原的语义模型)。
|
||||
// 两边同名但类型不同,直接 `as` 会在运行时 ClassCastException,所以必须显式转换。
|
||||
//
|
||||
// 字段对应证据:
|
||||
// · UserPropertiesCC:module_47243 的 codec `Gs.t7h` 按 wire 字段顺序写入模型槽位,
|
||||
// 同模块 `fi(lg).toString` 给出槽位 → 语义名。逐位对应:
|
||||
// Field1 bazookaGearScore, Field2 canUseGroup, Field3 crystals, Field4 crystalsRating,
|
||||
// Field5 droneGearScore, Field6 eventPassEndTime, Field7 gearScore, Field8 goldsTakenRating,
|
||||
// Field9 hasSpectatorPermissions, Field10 hullGearScore, Field11 id, Field12 partnerUid,
|
||||
// Field13 rank, Field14 rankBounds, Field15 registrationTimestamp, Field16 resistanceGearScore,
|
||||
// Field17 score, Field18 scoreRating, Field19 uid, Field20 userProfileUrl,
|
||||
// Field21 userRating, Field22 weaponGearScore.
|
||||
// 原包 `toString` 里 rankBounds 是嵌套对象,`he4().toString()`;状态层拆成 rankBounds / topBound 两个字符串。
|
||||
// · ReferralEventUserModelCC:wire 命名字段与 App.State 顺序一致(Field6/7/8 对应
|
||||
// referralCategory / referralQuestsSize / referrerCategory)。
|
||||
// · GaragePreviewModelCC:Field1..4 是相机参数(float),状态层用 Int/Int/Double/Int 收口,
|
||||
// GarageBox / SkyboxFrontSide 是资源 id(long)。
|
||||
package App.Session
|
||||
|
||||
import App.State.GaragePreviewModelCC as StateGaragePreview
|
||||
import App.State.ReferralEventUserModelCC as StateReferralEvent
|
||||
import App.State.UserPropertiesCC as StateUserProperties
|
||||
import Net.Generated.GaragePreviewModelCC as WireGaragePreview
|
||||
import Net.Generated.ReferralEventUserModelCC as WireReferralEvent
|
||||
import Net.Generated.UserPropertiesCC as WireUserProperties
|
||||
|
||||
object WireAdapters {
|
||||
|
||||
fun ToUserProperties(V: WireUserProperties): StateUserProperties = StateUserProperties(
|
||||
bazookaGearScore = V.Field1,
|
||||
canUseGroup = V.CanUseGroup,
|
||||
crystals = V.Field3,
|
||||
crystalsRating = V.Field4,
|
||||
droneGearScore = V.Field5,
|
||||
eventPassEndTime = V.Field6.toInt(),
|
||||
gearScore = V.Field7,
|
||||
goldsTakenRating = V.Field8,
|
||||
hasSpectatorPermissions = V.HasSpectatorPermissions,
|
||||
hullGearScore = V.Field10,
|
||||
id = V.Field11,
|
||||
partnerUid = V.PartnerUid,
|
||||
rank = V.Field13,
|
||||
rankBounds = V.RankBounds.Field1.toString(),
|
||||
topBound = V.RankBounds.Field2.toString(),
|
||||
registrationTimestamp = V.Field15,
|
||||
resistanceGearScore = V.Field16,
|
||||
score = V.Field17,
|
||||
scoreRating = V.Field18,
|
||||
uid = V.UID,
|
||||
userProfileUrl = V.UserProfileUrl,
|
||||
userRating = V.Field21,
|
||||
weaponGearScore = V.Field22,
|
||||
)
|
||||
|
||||
fun ToReferralEvent(V: WireReferralEvent): StateReferralEvent = StateReferralEvent(
|
||||
hideReferralCategory = V.HideReferralCategory,
|
||||
hideReferrerCategory = V.HideReferrerCategory,
|
||||
hidedAnnounce = V.HidedAnnounce ?: 0L,
|
||||
promoCodeDialogBackGround = V.PromoCodeDialogBackGround,
|
||||
promoCodeDialogImage = V.PromoCodeDialogImage,
|
||||
referralCategory = V.Field6,
|
||||
referralQuestsSize = V.Field7,
|
||||
referrerCategory = V.Field8,
|
||||
referrerPanelTitle = V.ReferrerPanelTitle,
|
||||
)
|
||||
|
||||
fun ToGaragePreview(V: WireGaragePreview): StateGaragePreview = StateGaragePreview(
|
||||
cameraAltitude = V.Field1.toInt(),
|
||||
cameraDistance = V.Field2.toInt(),
|
||||
cameraFov = V.Field3.toDouble(),
|
||||
cameraPitch = V.Field4.toInt(),
|
||||
garageBox = V.GarageBox,
|
||||
skyboxFrontSide = V.SkyboxFrontSide,
|
||||
)
|
||||
}
|
||||
@@ -7,28 +7,27 @@
|
||||
//
|
||||
// 覆盖范围:仅自动可判定的组合;其余协议模型需要 CC → 多字段的转换动作,
|
||||
// 见 names/downlink_actions.json 的 Models 清单(后续按协议逐个补转换器)。
|
||||
//
|
||||
// wire 模型(Net.Generated)与状态层模型(App.State)同名但类型不同;
|
||||
// 需要转换的模型走 App/Session/WireAdapters.kt,生成器里登记在 Adapters 表。
|
||||
package App.State
|
||||
|
||||
/** 每条记录解码后,用 (Any) -> Any? 构造对应动作;未覆盖的 methodId 不在表里。 */
|
||||
val DownlinkActions: Map<Long, (Any) -> Any?> = mapOf(
|
||||
8836555743203700004L to { Value -> InitGarageResources(Value as GaragePreviewModelCC) },
|
||||
2940606982196178690L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
2940608322101861340L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
1880027114297758713L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
276076212290602640L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
7864195052961084235L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
3982372259754314184L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
6492680006777979140L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
5628691596950015452L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
7864034006236084309L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
3977381235093458189L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
848734776772436751L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
8468360636859619961L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
2940606982196178690L to { Value -> SetReferralEventData(App.Session.WireAdapters.ToReferralEvent(Value as Net.Generated.ReferralEventUserModelCC)) },
|
||||
1880027114297758713L to { Value -> SetReferralEventData(App.Session.WireAdapters.ToReferralEvent(Value as Net.Generated.ReferralEventUserModelCC)) },
|
||||
276076212290602640L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
7864195052961084235L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
3982372259754314184L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
6492680006777979140L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
3977381235093458189L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
848734776772436751L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
8468360636859619961L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
)
|
||||
|
||||
/** Pt/Lt 容器里的 modelId → 动作(同一批协议模型的容器形态)。 */
|
||||
val DownlinkModelActions: Map<Long, (Any) -> Any?> = mapOf(
|
||||
3730185794267514852L to { Value -> InitGarageResources(Value as GaragePreviewModelCC) },
|
||||
5167916989664044333L to { Value -> SetReferralEventData(Value as ReferralEventUserModelCC) },
|
||||
1893408944113965505L to { Value -> SetUserPropertiesAction(Value as UserPropertiesCC) },
|
||||
3730185794267514852L to { Value -> InitGarageResources(App.Session.WireAdapters.ToGaragePreview(Value as Net.Generated.GaragePreviewModelCC)) },
|
||||
5167916989664044333L to { Value -> SetReferralEventData(App.Session.WireAdapters.ToReferralEvent(Value as Net.Generated.ReferralEventUserModelCC)) },
|
||||
1893408944113965505L to { Value -> SetUserPropertiesAction(App.Session.WireAdapters.ToUserProperties(Value as Net.Generated.UserPropertiesCC)) },
|
||||
)
|
||||
Reference in new issue
Block a user