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
Ananta-ps/internal/gameserver/routers/game_router_enemy_stiff.go
T

120 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}