using System.Buffers.Binary;
using Ananta.SDK.Rpc;
using Ananta.Server.Protocol.Client4229938;
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
using SceneMethods = Ananta.Server.RpcTypes.Client4229938.Methods.GameScene;
namespace Ananta.Server.Handlers.Game;
///
/// 迷雾 / 探索 POI / Aether 氛围层 —— SyncEnterFogMapPoiId(63491307)、
/// GmUnlockAllFogMap(65914419)、GmSetAmbianceEnabled(69166506)、
/// GmGetAmbianceEnabled(69384152)。
///
/// 移植自捐赠端 DremkaPS(2026-09-24)。
///
/// ★★ 这一组是抓包筛出来的,不是照单全收。捐赠端比本服多的 handler 一共有 66 个,
/// 拿 16.5 万行 unknown-methods.log 逐个数下来,只有 6 个真的被客户端发过:
/// SyncEnterFogMapPoiId 49 次(活跃到 09-23)、GmUnlockAllFogMap 5 次、
/// ForceSubmitTask 5 次、GmSwitchSpiritHere 3 次、GmSubmitEvent 2 次、
/// GmGetAmbianceEnabled 2 次。剩下 60 个零次,注册它们只是增加维护面 —— 已剔除。
///
/// 剔除的三条(虽然日志里出现过):
/// · ForceSubmitTask / GmSubmitEvent —— 任务族。本服
/// GameRouter.Tasks.cs 已经四次证明任务面板在 4229938 下渲染不出来
/// (TaskPublishEnabled = false),捐赠端自己也只拿它们记账,搬过来是死代码。
/// · GmSwitchSpiritHere —— 切角色。本服已有 AskSwitchSpirit 这条正规路径,
/// GM 版只是控制台的快捷方式,09-16 之后再没被用过。
///
/// ⛑ GmGetAmbianceEnabled 是 0 字节的 INVOKE:客户端在 await 一个返回值。
/// 不接它,控制台按下去会一直卡到超时(规则 94)。这里回一个 bool。
///
internal sealed partial class GameRouter
{
[Handler(MethodId.SyncEnterFogMapPoiId, HandlerPacketKind.Notify)]
private Task EnterFogMapPoiId(Connection conn, UxRpcMessage msg)
{
var poiId = msg.TryGetArgs(out var args) && args is not null
? args.PoiId
: (msg.Body.Length >= 4 ? BinaryPrimitives.ReadUInt32LittleEndian(msg.Body) : 0u);
if (poiId == 0)
{
conn.Log.Warn($"[FOG] enter-poi without an id bytes={msg.Body.Length}");
return Task.CompletedTask;
}
var state = GetWorldState(msg.Context);
bool fresh;
int total;
lock (state.SyncRoot)
{
fresh = state.FogMapPoiIds.Add(poiId);
total = state.FogMapPoiIds.Count;
}
// 同一个 POI 玩家会反复踏入(抓包里 49 条几乎全是 id=11),所以只在新增时记 Info。
if (fresh)
conn.Log.Info($"[FOG] enter-poi id={poiId} total={total}");
return Task.CompletedTask;
}
[Handler(MethodId.GmUnlockAllFogMap, HandlerPacketKind.Invoke)]
private async Task GmUnlockAllFogMap(Connection conn, UxRpcMessage msg)
{
var unlock = msg.TryGetArgs(out var args) && args is not null
? args.Unlock
: (msg.Body.Length > 0 && msg.Body[0] != 0);
var state = GetWorldState(msg.Context);
lock (state.SyncRoot)
state.FogMapAllUnlocked = unlock;
conn.Log.Info($"[FOG] gm-unlock-all unlock={unlock} poi={state.FogMapPoiIds.Count} "
+ $"raid={state.ActiveRaidId} (recorded: pushing SyncSceneFogMapAllUnlock needs the current sceneId, not wired yet)");
await conn.ReturnEmptyOkAsync(msg);
}
[Handler(MethodId.GmSetAmbianceEnabled, HandlerPacketKind.Invoke)]
private async Task GmSetAmbianceEnabled(Connection conn, UxRpcMessage msg)
{
bool enabled;
int type = 0;
if (msg.TryGetArgs(out var args) && args is not null)
{
enabled = args.Enabled;
type = args.Type;
}
else
{
enabled = msg.Body.Length > 0 && msg.Body[0] != 0;
}
var state = GetWorldState(msg.Context);
lock (state.SyncRoot)
{
state.AmbianceEnabled = enabled;
state.AmbianceType = type;
}
conn.Log.Info($"[GM] ambiance set enabled={enabled} type={type}");
await conn.ReturnEmptyOkAsync(msg);
}
///
/// 0 字节 INVOKE —— 必须回一个 bool,否则客户端这次调用会 await 到超时。
///
[Handler(MethodId.GmGetAmbianceEnabled, HandlerPacketKind.Invoke)]
private Task GmGetAmbianceEnabled(Connection conn, UxRpcMessage msg)
{
var state = GetWorldState(msg.Context);
bool enabled;
lock (state.SyncRoot)
enabled = state.AmbianceEnabled;
conn.Log.Info($"[GM] ambiance query -> {enabled}");
return conn.ReturnAsync(msg, enabled);
}
}