143 lines
8.0 KiB
Go
143 lines
8.0 KiB
Go
package routers
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
)
|
||
|
||
var _ = sync.Mutex{}
|
||
var _ = context.Background
|
||
|
||
// Generic character switching. No protagonist-specific switch path lives here.
|
||
//
|
||
// 面板「切人演出」指定要播的那一条 SwitchSpiritConfig 行;0 = 照老规矩自己挑
|
||
// (距离 / DisperseAnchorsByCharacter / 权重)。
|
||
//
|
||
//
|
||
// true = 真的把切人演出(SyncPreSwitchSpirit / SyncSwitchSpiritConfigId)推出去了;
|
||
// false = 什么演出都没播(世界没就绪 / 上一次还 pending / 角色不在可控花名册 /
|
||
// 目标就是当前角色 / 演出开关关着 / 退化成静默换皮 / 锚点超限)。
|
||
// 面板「切人演出」卡靠这个返回值给玩家一句人话,而不是一律回「已播放」。
|
||
//
|
||
func GameRouter_Switching_OnSwitchSpirit(ctx context.Context, rpcCtx *RpcContext, requestedSpiritId uint32, allowShow bool, forcedSwitchShowId uint32) bool {
|
||
// Router implementation for GameRouter_Switching_OnSwitchSpirit
|
||
return true
|
||
}
|
||
|
||
//
|
||
// Phase 2:客户端播完 pre-switch Timeline。落点早已定好(见 OnSwitchSpirit),
|
||
// 这里只把控制权交给已经站在落点上的那个角色,不再把它挪回玩家发起切人时的位置。
|
||
//
|
||
func GameRouter_Switching_OnPreSwitchSpiritFinish(ctx context.Context, rpcCtx *RpcContext) any {
|
||
// Router implementation for GameRouter_Switching_OnPreSwitchSpiritFinish
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// 撤掉这出演出 spawn 出来的演员(WorldEntryState.PendingSwitchShowActors)。
|
||
// ⛑ 移除通道与召唤物一致:SyncLogicAgentLeave(68281244) —— 这类单位是
|
||
// SpawnAgentAsync 出来的,用 SyncAetherAINpcRemove(ambient 行人通道)发了等于没发。
|
||
// 幂等:列表清空后重复调用无副作用。
|
||
//
|
||
func GameRouter_Switching_DismissSwitchShowActorsAsync(ctx context.Context, rpcCtx *RpcContext, state any, why string) any {
|
||
// Router implementation for GameRouter_Switching_DismissSwitchShowActorsAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
// 演出没等到回执(丢包 / 客户端没播):按已定好的落点直接完成移交。
|
||
func GameRouter_Switching_CompleteSwitchWithoutTimelineAsync(ctx context.Context, rpcCtx *RpcContext, state any, reason string) any {
|
||
// Router implementation for GameRouter_Switching_CompleteSwitchWithoutTimelineAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
func GameRouter_Switching_ClearPendingSwitch(ctx context.Context, state any) any {
|
||
// Router implementation for GameRouter_Switching_ClearPendingSwitch
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Server-driven character switch, used by the admin panel instead of the in-game wheel.
|
||
// The wheel (Z) is client-side only: it lists FightSpiritConfig rows whose CanJoin is true
|
||
// (15 of 40 here) and switching from it never tells the server anything. Publishing a
|
||
// SyncTaskRoleTeam roster cannot widen it either — a non-empty team greys out every entry the
|
||
// team does not name (see TOPIC_切换角色.md §6), which is worse than leaving it alone.
|
||
// So the panel drives OnSwitchSpirit directly: same handoff the client's own
|
||
// AskSwitchSpirit takes, but without the client having to offer the character first. That
|
||
// means every roster row is reachable, including the CanJoin=false ones (Bansy, Aileen,
|
||
// Mimiko, Ringo, ...) and rows kept out of the roster by the placeholder-icon filter until
|
||
// they are added through content.roster.additionalTemplateIds (15021042 Lemon Sour).
|
||
//
|
||
func GameRouter_Switching_SwitchSpiritAsync(ctx context.Context, session *GameSession, spiritId uint32) any {
|
||
// Router implementation for GameRouter_Switching_SwitchSpiritAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// 面板「切人演出」卡片专用:**指定**要播的那一条 SwitchSpiritConfig 行。
|
||
// ⛑ 与上面的 SwitchSpiritAsync 唯一的区别就是 allowShow:true +
|
||
// forcedSwitchShowId —— 那条是「静默换皮」(面板挑人用),这条是「播演出」
|
||
// (会发 SyncPreSwitchSpirit / SyncSwitchSpiritConfigId,并把玩家落到该行的 Position 锚点)。
|
||
// ⛑ 201 行里 AgentId 非空的行服务端这条链路补不出随从(见 ClientConfigRepository 注释),
|
||
// 面板默认不列这类行;真指定了也只是演出里少了那几个陪衬角色,不会崩。
|
||
//
|
||
func GameRouter_Switching_SwitchSpiritWithShowAsync(ctx context.Context, session *GameSession, spiritId uint32, switchShowId uint32) any {
|
||
// Router implementation for GameRouter_Switching_SwitchSpiritWithShowAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// 客户端 GM 控制台的「就地切人」:GmSwitchSpiritHere(69758035),
|
||
// 参数是 uint32 的 FightSpirit id(ClientToGameSceneGMDelegate.lua:2132)。
|
||
// 抓包样本(logs/unknown-methods.log):hex=D337E500 → 15020499、
|
||
// hex=2C7D6402、hex=00000000。共 3 次 INVOKE,此前一直是
|
||
// unimplemented —— 控制台按下去只回一个空 ack,什么都不会发生。
|
||
// ⛑ 这是客户端自己的原生入口(GM 面板 324 个方法之一),不是我们造的:
|
||
// 它证明官方确实有一条「不播演出、就地换人」的切人路径。接上它等于拿到一条
|
||
// 完全原生的切人通道,可以拿来跟 AskSwitchSpirit 那条 14 秒演出路径做对照。
|
||
// "Here" = 就地:走与 AskSwitchSpirit 完全相同的身份移交,但
|
||
// allowShow:false ⇒ 不发 SyncPreSwitchSpirit / SyncSwitchSpiritConfigId,
|
||
// 不播 LiftOff 演出、不传送到演出锚点(换皮 + 原地移交)。
|
||
//
|
||
func GameRouter_Switching_GmSwitchSpiritHere(ctx context.Context, conn any, msg any) any {
|
||
// Router implementation for GameRouter_Switching_GmSwitchSpiritHere
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// Publish the character-wheel roster through SyncTaskRoleTeam (64534724).
|
||
// Pressing Z sends nothing to the server — the rotation list is built entirely client-side, and
|
||
// because retail never called this method the client has always used its own fallback: the
|
||
// FightSpiritConfig rows with CanJoin=true (exactly the 15 characters the player sees, including
|
||
// Yinglong but not Mimiko). This notify is the one server-side handle on that list
|
||
// (SpiritManager.lua:233 <- GameToClientImpl.lua:2311 fills taskRoleTeam from enableRoleIds).
|
||
// Opt-in only: an empty roster config means nothing is sent and the client keeps its default.
|
||
// Non-empty takes the list over wholesale, so anything to keep has to be listed too.
|
||
// Re-published on every scene load because lua does table.clear + refill — it is idempotent by
|
||
// design, and re-sending is how the list survives a scene change.
|
||
//
|
||
func GameRouter_Switching_PublishSwitchRoleTeamAsync(ctx context.Context, rpcCtx *RpcContext) any {
|
||
// Router implementation for GameRouter_Switching_PublishSwitchRoleTeamAsync
|
||
var zero any; return zero
|
||
}
|
||
|
||
func GameRouter_Switching_SwitchSpiritDirectSameScene4229938(ctx context.Context, rpcCtx *RpcContext, state any, templateId uint32, unitId uint64, switchPosition Vec3, switchFacing float32, switchShowId uint32, targetInWorld bool) any {
|
||
// Router implementation for GameRouter_Switching_SwitchSpiritDirectSameScene4229938
|
||
var zero any; return zero
|
||
}
|
||
|
||
//
|
||
// ★ AskSwitchSpiritComplete(67272187) —— 客户端「整条切人演出播完了」的回执。
|
||
// ⛑⛑ 以前它是**空转**的:`GameRouter.UnhandledBatch.cs` 把它挂在 SkillNeutralAccepts
|
||
// 那个「只打一行 [COMBAT] accept 就 ReturnEmptyOkAsync」的兜底处理器上
|
||
// ⇒ 演出到底播没播完、播了多久,服务端完全不知道(09-26 00:2x 评估 CBT3 资料时发现的盲区)。
|
||
// CBT3 那份 `切人传送时机_apex传送实现.md` 用它标定 apexDelay;我们现在没有 apex 传送,
|
||
// 但这条时间戳本身就是「演出真实时长」的唯一实测来源 —— 先记下来,别再丢。
|
||
// ⛑ 客户端在切人流程里一共只发两个 C2S:这个 + ReportPreSwitchSpiritFinish(67654364)。
|
||
// ReportPreTeleportFinish(63032762) **不发**(那是传送流程的),别拿它当切人信号。
|
||
//
|
||
func GameRouter_Switching_AskSwitchSpiritComplete(ctx context.Context, conn any, msg any) any {
|
||
// Router implementation for GameRouter_Switching_AskSwitchSpiritComplete
|
||
var zero any; return zero
|
||
}
|
||
|