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_toilet_qte.go
T

175 lines
9.4 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
//
// 厕所 QTE / 马桶鸵鸟骑乘(2026-09-30 新增)。
// <para>★ 玩法本体:ToiletOstrichGamePanelStore.lua —— 骑上马桶鸵鸟后弹一个音游式三键 QTE
// (QTE_DURATION=2 / PERFECT_WINDOW=0.1 / GREAT_WINDOW=0.2,满分 100),
// 失败时鸵鸟逃跑。面板与判定全在客户端,服务端要做的是「让骑乘坐实」+「接住逃跑上报」。</para>
// <para>★ 触发链(三条证据实锤,见 GameMethods.AskRideAgent4229938 的契约注释)</para>
// <list type="number">
// <item>面板由 C#→lua 回调打开:CsToLuaHandler.lua 的
// module.OnStartRideQTE(riderPid, beRidenPid) ⇒
// gPanelManager:CheckShow(gPanelId.TOILET_OSTRICH_GAME_PANEL, { npcPid = beRidenPid })。
// 也就是说 C# 认定「骑乘成立」时才 fire,lua 只是被动开面板。</item>
// <item>面板里 SceneDataMgr.GetUnit(self.npcPid) ⇒ 被骑的那只必须是世界里真实存在的 unit。</item>
// <item>失败分支会主动上报 AskToiletOstrichFlee;成功分支只发本地信号 5753。</item>
// </list>
// <para>★ 为什么不需要新的生成通道</para>
// 马桶鸵鸟(40122028 / 40652007 / 40922011 / 42046401 / 45000446,规范 id
// AgentConfig.ToiletOstrich = 42046401)在 NpcCatalog.json 里是标准 Agent
// (GeneralModelId=86963029、EnemyClassType=1、faction 18000300「通用怪物」),
// 而且 AgentSummonCatalog.json 里本来就有它 ⇒ 直接用既有的服务端直生成通道
// (GameAdminBridge.SpawnAgentAsync)就能出,客户端 ai 状态树
// (MassAI.Npc.MassAetherAI.Tasks.TryOnRideAgent / TryDownRideAgent,走
// BehaviorActionConfigId 40214321 + AISetting 40213146)自带骑乘能力,服务端不用给标记。
// 面板入口见 GameAdminBridge.AgentSummonCatalog(它被置顶到「生成敌人」下拉框第一位)。
// <para>★ 一次只动一个(实测顺序,按这个走)</para>
// <list type="number">
// <item>生成 42046401 → 走到旁边按骑乘。</item>
// <item>面板出来了 ⇒ 本文件的默认配置已经够用,接下来看
// [TOILET] 日志里 flee 出现时鸵鸟是否正确消失。</item>
// <item>面板不出来 ⇒ 把 gameplay.toiletQte.pushRideState 打开(推
// SyncRideAgent(rider, ridee, true)),这与自动驾驶那条「服务端推状态」是同一个模式;
// ⛑ 注意 GameRouter.VehicleNav.cs §43.1 的包序教训:状态包要排在动作包之前,
// 所以这里也是先推状态、再回 ack。</item>
// </list>
// <para>⛑ 回滚:把本文件涉及的三个 mid 从 GameRouter.cs 的
// Enabled4229938MethodIds 里删掉即可(handler 不再注册 = 回到
// ReturnEmptyOkAsync / DefaultUnknownNotify 兜底,不在场时行为与今天之前一致);
// 或者一个开关 gameplay.toiletQte.enabled=false 只关「回头推 + 回收」,不动注册。</para>
//
// 模板是不是马桶鸵鸟。GameAdminBridge.SpawnAgentAsync 用它决定「生成成坐骑还是生成成敌人」。
func GameRouter_ToiletQte_IsToiletOstrichAgent(ctx context.Context, agentId uint32) bool {
// Router implementation for GameRouter_ToiletQte_IsToiletOstrichAgent
return true
}
// 当前正骑着哪只(session 级)。下马/逃跑时要靠它知道该撤哪个 uid 的状态。
func GameRouter_ToiletQte_GetRidingMount(ctx context.Context, session *GameSession) uint64 {
// Router implementation for GameRouter_ToiletQte_GetRidingMount
return 0
}
func GameRouter_ToiletQte_SetRidingMount(ctx context.Context, session *GameSession, uid uint64) any {
// Router implementation for GameRouter_ToiletQte_SetRidingMount
var zero any; return zero
}
//
// 建立/解除完整骑乘状态(② + ③④)。顺序照抄捐赠端:SyncRideAgent 先,345/346 后。
// ⛑⛑ 必须在 ReturnEmptyOkAsync **之前**发完 —— §43.1 的包序教训。
//
func GameRouter_ToiletQte_PublishToiletRideAsync(ctx context.Context, conn any, rider uint64, mount uint64, enter bool) any {
// Router implementation for GameRouter_ToiletQte_PublishToiletRideAsync
var zero any; return zero
}
//
// uid → 模板 id,**只为马桶鸵鸟**记一条。
// <para>⛑ 为什么不直接查敌人账本:坐骑模式是 engage=false(不下令开战、不登记进
// LocalEnemyRuntimeState.Enemies),账本里根本没有它;而 AskRideAgent /
// AskToiletOstrichFlee 收到的载荷只有 unit uid,没有模板 id ⇒ 日志里
// isOstrich=? 就再也判不出来了。</para>
// 会话销毁即失效(存在 session.Items 里),本来也是一次性的东西。
//
func GameRouter_ToiletQte_ToiletMounts(ctx context.Context, session *GameSession) map[string]any {
// Router implementation for GameRouter_ToiletQte_ToiletMounts
return nil
}
// 面板生成马桶鸵鸟时登记一条(由 GameAdminBridge.SpawnAgentAsync 调用)。
func GameRouter_ToiletQte_NoteToiletMountSpawned(ctx context.Context, session *GameSession, uid uint64, agentId uint32) any {
// Router implementation for GameRouter_ToiletQte_NoteToiletMountSpawned
var zero any; return zero
}
//
// uid 是不是本服自己生成的(GameAdminBridge.ServerSpawnUidBase 起的池子)。
// <para>⛑⛑ 2026-09-30 实测事故:AskToiletOstrichFlee 送来过 uid=19000,
// 而 19000 是场景自带的真实敌人([LENEMY] ... uid=19000 serverSpawned=False hp=1500,
// 在 -4739,169,-2828),当时代码无条件回收 ⇒ 把一只无辜的场景敌人删了。
// 根因是 AskRideAgent 我们一律回 0 字节成功,客户端于是对任何可按 F 的单位
// 都 fire OnStartRideQTE,失败后又对那个单位发 flee。</para>
// <para>⇒ 回收的硬门槛改成「只回收本服生成的单位」:客户端这条 Notify 只在马桶 QTE
// 失败时发,而我们的马桶一定是我们自己 spawn 的,两条判据交集足够;场景单位落在池子外,
// 天然被排除。ServerSpawnUidBase = 0x200000000000(= 35184372088832)远高于任何
// 场景单位 uid。</para>
//
func GameRouter_ToiletQte_IsServerSpawnedUid(ctx context.Context, uid uint64) bool {
// Router implementation for GameRouter_ToiletQte_IsServerSpawnedUid
return true
}
// ========================================================================================
// AskRideAgent(67575069) —— C2S Invoke,载荷 ulong agentId,**回包必须是 0 字节**
// ========================================================================================
func GameRouter_ToiletQte_AskRideAgent4229938Handler(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_ToiletQte_AskRideAgent4229938Handler
var zero any; return zero
}
// ========================================================================================
// AskStopRideAgent(67449345) —— C2S Invoke,**无参**,回包也是 0 字节
// ========================================================================================
func GameRouter_ToiletQte_AskStopRideAgent4229938Handler(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_ToiletQte_AskStopRideAgent4229938Handler
var zero any; return zero
}
// ========================================================================================
// AskToiletOstrichFlee(67154315) —— C2S **Notify**,载荷 ulong agentid(QTE 失败时客户端上报)
// ========================================================================================
func GameRouter_ToiletQte_AskToiletOstrichFlee4229938Handler(ctx context.Context, conn any, msg any) any {
// Router implementation for GameRouter_ToiletQte_AskToiletOstrichFlee4229938Handler
var zero any; return zero
}
// ------------------------------------------------------------------ helpers
//
// 当前「骑的人」是谁 —— 也就是 SyncRideAgent 的 rider。
// 出场单位优先(WorldEntryControlUnit),没有就退回当前操控体
// (ActiveSpiritUnitId,切人后它会跟着变)。拿不到返回 0(日志里就是 0,此时不推状态包)。
//
func GameRouter_ToiletQte_CurrentRiderUnit(ctx context.Context, session *GameSession) uint64 {
// Router implementation for GameRouter_ToiletQte_CurrentRiderUnit
return 0
}
//
// 读出 AskRideAgent 里的单位 id。
// ⛑ AskStopRideAgent 没有载荷,别拿它来调这个方法。
// 解析失败不能抛:这是客户端**上报**类请求,服务端炸了会把整帧链路带断(规则 168 的同族问题)。
//
func GameRouter_ToiletQte_TryReadRideTarget(ctx context.Context, msg any) uint64 {
// Router implementation for GameRouter_ToiletQte_TryReadRideTarget
return 0
}
//
// 读出 AskToiletOstrichFlee 里的单位 id。
// ⛑ 两条载荷在线上字节相同(都是单个 u64),但**各用各的契约** —— 互相借会在将来某一方
// 加字段时静默解析错位,而这里正好是「客户端上报」路径,错了只会静默丢事件。
//
func GameRouter_ToiletQte_TryReadFleeTarget(ctx context.Context, msg any) uint64 {
// Router implementation for GameRouter_ToiletQte_TryReadFleeTarget
return 0
}
//
// uid → 模板 id。先查马桶鸵鸟登记表(坐骑模式不 engage ⇒ 不进敌人账本),再查敌人账本,
// 都没有返回 0(日志里打成 ?,那是正常的,不代表解析失败)。
//
func GameRouter_ToiletQte_LookupEnemyTemplate(ctx context.Context, session *GameSession, uid uint64) uint32 {
// Router implementation for GameRouter_ToiletQte_LookupEnemyTemplate
return 0
}