118 lines
5.5 KiB
C#
118 lines
5.5 KiB
C#
using Ananta.SDK.Network;
|
||
using Ananta.SDK.Rpc;
|
||
using Ananta.Server.Configuration;
|
||
using Ananta.Server.Protocol.Client4229938;
|
||
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
|
||
|
||
namespace Ananta.Server.Handlers.Game;
|
||
|
||
/// <summary>
|
||
/// 掉落物拾取的两个回执 —— 2026-09-25 接入。
|
||
///
|
||
/// <b>起因</b>:用户回报「路人 NPC 手里的手机能打落拾取,但他们的 BBChat 点不开」。
|
||
/// 掉手机这件事客户端本地就能做(GM 命令 <c>DebugEnableDropPhoneWhenDeath</c>,
|
||
/// <c>补丁和工具/ClientGmCommands.txt:320</c>),掉出来的手机是客户端自己生成的 SceneItem;
|
||
/// 但拾取时它发了两个 <b>INVOKE</b> 给服务端:
|
||
/// <code>
|
||
/// AskPickUpSceneItemTransferToItem (67297154) u64 sceneitemuid
|
||
/// AskEnemyItemPickUp (67453017) u64 enemyinstanceid + i32 binditemsindex
|
||
/// </code>
|
||
/// 这两个 MethodId 在本服务端<b>零 handler、也不在白名单</b>(全项目 grep 只命中 MethodId.cs),
|
||
/// ⇒ 走 <c>DefaultUnknownInvoke</c> 兜底或直接 await 挂起(硬规则 94)。
|
||
///
|
||
/// <b>本文件只回空 OK,不做内容</b>。理由(见 2026-09-25 记忆 §路人手机调研):
|
||
/// NPC 手机里的聊天<b>一个字节都不走服务端</b> ——
|
||
/// <c>GUI/Chat/RandomChatManager.lua:13 GetChatId(npcId, itemId)</c> 用
|
||
/// <c>AgentConfig.GetConfig(npcId)</c> 的 AppearanceTag / BodyTag / SexType / JobTag 去匹配
|
||
/// 35 行 <c>NPCChatAtmosphereNewConfig</c>(84028000-84028034),按 <c>60/Weight</c> 加权
|
||
/// 随机挑 1 条 <c>NPCChatConfig</c> 聊天链,然后打开面板。
|
||
/// ⇒ 想让"每个人手机不一样",要动的是 <b>npcId(AgentConfig id)</b>,不是往服务端塞文本。
|
||
///
|
||
/// ⛑ 注册的 kind 用 <c>Both</c>:按 AskControlAgent 那次的教训,同族 RPC 会有 NOTIFY 变体,
|
||
/// 只注册 Invoke 就是静默丢包;<c>ReturnEmptyOkAsync</c> 对 NOTIFY 是 no-op。
|
||
/// </summary>
|
||
internal sealed partial class GameRouter
|
||
{
|
||
/// <summary>per-session:本局拾取过的掉落物 uid(去重后只用于日志计数)。</summary>
|
||
private const string SceneItemPickupStateKey = "client4229938-sceneitem-pickup";
|
||
|
||
/// <summary>
|
||
/// AskPickUpSceneItemTransferToItem(67297154) —— 把场景里的掉落物收进背包。
|
||
///
|
||
/// 客户端本地掉落、本地回收,服务端没有 SceneItem 账本(我们从未推过
|
||
/// <c>SyncEnemyBeginSceneItemDrop</c> 68357238 / <c>SyncDropBelonging</c> 68316751),
|
||
/// 所以这里能做的只有<a href="硬规则 94">放行</a>:不回执,拾取动作 await 到超时。
|
||
/// </summary>
|
||
[Handler(MethodId.AskPickUpSceneItemTransferToItem, HandlerPacketKind.Both)]
|
||
private async Task OnAskPickUpSceneItemTransferToItem(Connection conn, UxRpcMessage msg)
|
||
{
|
||
ulong uid = 0;
|
||
try
|
||
{
|
||
var args = msg.GetArgs<GameMethods.AskPickUpSceneItemTransferToItem4229938>();
|
||
if (args is not null)
|
||
uid = args.sceneitemuid;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// ⛑ 不 throw:这是 INVOKE,抛异常等于不回执 ⇒ 客户端卡死。解析不出 uid 也要放行。
|
||
conn.Log.Warn($"[PICKUP] AskPickUpSceneItemTransferToItem 解析失败(按 uid=0 处理): {ex.Message}");
|
||
}
|
||
|
||
await conn.ReturnEmptyOkAsync(msg);
|
||
|
||
if (!PrivateServerConfigStore.Current.Gameplay.SceneItemPickupEnabled)
|
||
return;
|
||
|
||
var items = msg.Context.Session.Items;
|
||
if (items.TryGetValue(SceneItemPickupStateKey, out var raw) is false || raw is not HashSet<ulong> set)
|
||
{
|
||
set = [];
|
||
items[SceneItemPickupStateKey] = set;
|
||
}
|
||
|
||
bool fresh;
|
||
lock (set)
|
||
fresh = set.Add(uid);
|
||
|
||
conn.Log.Info($"[PICKUP] sceneitem uid={uid} kind={(msg.IsInvoke ? "invoke" : "notify")} " +
|
||
$"{(fresh ? "new" : "repeat")} — 本局已拾取 {set.Count} 件(服务端无 SceneItem 账本,只回执)");
|
||
}
|
||
|
||
/// <summary>
|
||
/// AskEnemyItemPickUp(67453017) —— 从敌人/NPC 身上拿走第 N 件携带物(belonging)。
|
||
///
|
||
/// 官方完整链是 <c>SyncAgentBelongings</c> → <c>SyncDropBelonging</c>(68316751) →
|
||
/// <c>SyncEnemyBeginSceneItemDrop</c>(68357238) → 本包。我们只发过第一步
|
||
/// (<c>GameRouter.HackNpc.cs</c> 给路人递手机),后两步是 T3-9 的整块待办。
|
||
/// 客户端仍会自发这个包 ⇒ 同样只放行。
|
||
/// </summary>
|
||
[Handler(MethodId.AskEnemyItemPickUp, HandlerPacketKind.Both)]
|
||
private async Task OnAskEnemyItemPickUp(Connection conn, UxRpcMessage msg)
|
||
{
|
||
ulong enemy = 0;
|
||
int index = -1;
|
||
try
|
||
{
|
||
var args = msg.GetArgs<GameMethods.AskEnemyItemPickUp4229938>();
|
||
if (args is not null)
|
||
{
|
||
enemy = args.enemyinstanceid;
|
||
index = args.binditemsindex;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
conn.Log.Warn($"[PICKUP] AskEnemyItemPickUp 解析失败(按 0/-1 处理): {ex.Message}");
|
||
}
|
||
|
||
await conn.ReturnEmptyOkAsync(msg);
|
||
|
||
if (!PrivateServerConfigStore.Current.Gameplay.SceneItemPickupEnabled)
|
||
return;
|
||
|
||
conn.Log.Info($"[PICKUP] belonging from enemy={enemy} index={index} " +
|
||
$"kind={(msg.IsInvoke ? "invoke" : "notify")}(belongings 是 HackNpc 递的手机,服务端无掉落账本)");
|
||
}
|
||
}
|