This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

119 lines
7.6 KiB
Go
Raw Permalink Normal View History

package routers
import (
"context"
"sync"
)
var _ = sync.Mutex{}
var _ = context.Background
//
// 硬直 / 虚弱 / 死亡表现 的回执与出口,2026-09-23 补上。
// 为什么单独一个文件:在此之前服务端只做"发射"——推伤害、推架势条、推死亡包——却没有一条
// 客户端回执的路径,于是客户端的状态机有入口无出口。玩家看到的三条症状全部出自这里:
// 1. 「击倒 / 打晕之后敌人卡住」:客户端用 ReportUnitStandUp(67329238) 和
// AskAgentExitStiff(67581079) 告诉我们"硬直结束了",而且 ReportUnitStandUp 是
// Invoke——客户端阻塞等回执。我们没有 handler,客户端的起身流程就一直挂着,
// 单位停在硬直动作里,既不受击也不攻击。
// 2. 「虚弱窗口关不掉」:破防时我们只改自己的账本,从不推 SyncEnemyDisarmState(true),
// 窗口到点也从不推 SyncEnemyDisarmState(false) + 架势条回满。客户端的虚弱状态是
// 靠这两个包进出场的(kimika 捐赠端 GameRouter.EnemyGroggy.cs 的完整闭环)。
// 3. 「死了之后敌人又会出现,变成打不死的状态」:SyncEnemyPrepareDie 只让客户端播死亡,
// 尸体要 SyncClearSpoonEnemies(68237191) 才拆得掉。我们从来没发过它,所以单位带着一条
// 空血条继续站在场上;而它的账本在死亡序列末尾已经被删,之后的命中无人结算
// (serverSpawned 且未注册 ⇒ 直接 return),就成了"打不死的幽灵"。
// 来源:kimika 捐赠端(2026-09-22 用户口述可信方)GameRouter.EnemyGroggy.cs / GameRouter.Enemy.cs。
// 与之不同的只有一点:捐赠端把 stand-up 注册成 HandlerPacketKind.Invoke,这里不带 kind 注册,
// 两种包形态都收、内部再判 IsInvoke 回执——只收 Invoke 会漏掉以 Notify 形态发来的那一次
// (本服务端自己的 AskEnemyStartFall 就是踩过这个才改成双形态的)。
//
// ---------------------------------------------------------------- 硬直 (stiff)
// AskAgentEnterStiff(67352229) — 客户端说这个单位进硬直了。Notify,不用回。
// 记下来是为了让攻击 AI 别在硬直里继续挥(GameRouter.EnemyAiActions)。
func GameRouter_EnemyStiff_LocalEnemyEnterStiff(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_EnemyStiff_LocalEnemyEnterStiff
var zero any; return zero
}
// AskAgentExitStiff(67581079) — 客户端的硬直结束了。Notify,但同样两种形态都收。
// ⛑⛑ 2026-09-23 17:3x:这里不再关闭虚弱窗口。日志证据(console-20260923-172435.log):
// uid=…836 在 17:30:04 破防,17:30:05 就被 client-exit-stiff 关掉 —— 窗口只活了 1 s,
// 因为 67149 一次性动作播完客户端立刻上报退出硬直。而我们正是靠周期重推 67149 来"撑"
// 这个窗口的,于是第一次破防之后每次都被自己提前结束 = "第一次正常,后面不正常"。
// 窗口的权威出口只剩两个:ReportUnitStandUp(真起身)和 groggy 超时兜底。
// (捐赠端 kimika 是拿它关窗口的,但它没有周期重推,所以不冲突;我们不能照搬。)
func GameRouter_EnemyStiff_LocalEnemyExitStiff(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_EnemyStiff_LocalEnemyExitStiff
var zero any; return zero
}
// ReportUnitStandUp(67329238) — 单位起身,Invoke,必须回执。
// 这是"虚弱结束"的权威信号:收到即关窗口,不等超时。
func GameRouter_EnemyStiff_LocalEnemyStandUp(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_EnemyStiff_LocalEnemyStandUp
var zero any; return zero
}
func GameRouter_EnemyStiff_TryReadStiffAgentId(ctx context.Context, msg any) uint64 {
// Router implementation for GameRouter_EnemyStiff_TryReadStiffAgentId
return 0
}
// ---------------------------------------------------------------- 虚弱 (groggy) 进出场
// 破防:推 SyncEnemyDisarmState(true) + 架势条 0。
// 客户端的虚弱 UI 在这个 build 里是半成品(捐赠端注释:SyncEnemyPoiseRate 的 C# 实现有反向
// guard、SyncEnemyDisarmState 的 C# 体是空的、CheckShowDisarmBar 还卡 maxDisarmValue<=0),
// 真正被客户端"听进去"的是这两条:它自己上报的硬直,以及敌人在这段时间里不再挥击。
// 账本侧(Poise 归零、GroggyUntilTicks)由调用方在锁内写好,这里只负责出场包。
func GameRouter_EnemyStiff_EnterLocalEnemyGroggyAsync(ctx context.Context, session *GameSession, uid uint64, reason string) any {
// Router implementation for GameRouter_EnemyStiff_EnterLocalEnemyGroggyAsync
var zero any; return zero
}
// 虚弱结束:清窗口、架势回满、推 SyncEnemyDisarmState(false) + 架势条 1。
// 幂等:对不在虚弱中的单位是 no-op,所以 stand-up 报告和超时不会重复推。
func GameRouter_EnemyStiff_ExitLocalEnemyGroggyAsync(ctx context.Context, session *GameSession, uid uint64, reason string) any {
// Router implementation for GameRouter_EnemyStiff_ExitLocalEnemyGroggyAsync
var zero any; return zero
}
// 每个 AI tick 调一次:关掉到期的虚弱窗口。stand-up 报告没来时这就是兜底出口。
// 由 GameRouter.EnemyAiActions 的 worker 驱动;worker 退出后若敌人还活着会由命中路径补。
func GameRouter_EnemyStiff_MaintainLocalEnemyGroggyAsync(ctx context.Context, session *GameSession, now int64) any {
// Router implementation for GameRouter_EnemyStiff_MaintainLocalEnemyGroggyAsync
var zero any; return zero
}
// ---------------------------------------------------------------- 死亡表现 / 尸体清理
// 拆尸体:SyncClearSpoonEnemies(68237191)。幂等——第一个调用者赢,后来的都是 no-op。
// 这是"死了之后敌人还站在场上"的唯一解药;SyncEnemyPrepareDie 只负责播动画。
func GameRouter_EnemyStiff_CompleteLocalEnemyDeathClearAsync(ctx context.Context, session *GameSession, uid uint64, reason string) any {
// Router implementation for GameRouter_EnemyStiff_CompleteLocalEnemyDeathClearAsync
var zero any; return zero
}
// ReportEnemyPrepareFinish(67427299) — 客户端说死亡准备流程走完了,立刻拆尸体。
func GameRouter_EnemyStiff_LocalEnemyPrepareFinish(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_EnemyStiff_LocalEnemyPrepareFinish
var zero any; return zero
}
// ReportAgentDiePerfEnd(67638502) — 客户端说死亡表现播完了(Invoke,必须回执,
// 否则客户端的死亡流程会挂住)。同样立刻拆尸体。
func GameRouter_EnemyStiff_LocalEnemyDiePerfEnd(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_EnemyStiff_LocalEnemyDiePerfEnd
var zero any; return zero
}
// 尸体清理的兜底扫描:客户端一个报告都没发来时,超时也要把尸体拆掉。
// 由攻击 AI worker 驱动。
// ⛑⛑ 2026-09-23 19:4x:这里曾经在扫描时顺手周期补推一次倒地动作,已撤销 —— 那是
// "远程杀死敌人放两次倒地动画"的一半原因。远程打完玩家就停手,hook 播的那次 88300 本来
// 完整播完了,周期补推又重启了一次。补推只能挂在"确实被顶掉了"的证据上,也就是客户端
// 的 AskAgentEnterStiff 上报(ReassertLocalEnemyDeathAsync,且带 900 ms 静默窗口)。
func GameRouter_EnemyStiff_SweepLocalEnemyDeathClearsAsync(ctx context.Context, session *GameSession, now int64) any {
// Router implementation for GameRouter_EnemyStiff_SweepLocalEnemyDeathClearsAsync
var zero any; return zero
}