feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -1,5 +1,7 @@
|
||||
using Ananta.SDK.Rpc;
|
||||
using Ananta.Server.Gameplay;
|
||||
using Ananta.Server.Protocol.Client4229938;
|
||||
using SceneMethods = Ananta.Server.RpcTypes.Client4229938.Methods.GameScene;
|
||||
|
||||
namespace Ananta.Server.Handlers.Game;
|
||||
|
||||
@@ -13,6 +15,7 @@ internal sealed partial class GameRouter
|
||||
{
|
||||
private Task DefaultUnknownInvoke4229938(RpcContext ctx)
|
||||
{
|
||||
UnknownMethodLogger.Log(ctx.Session.Log.Scope, "INVOKE", ctx.MethodId, ctx.Body);
|
||||
if (DefaultReturnCatalog4229938.TryGet(ctx.MethodId, out var body, out var shape))
|
||||
{
|
||||
ctx.Session.Log.Warn($"[RPC4229938] unimplemented {Ananta.SDK.Logging.RpcMethodNames.Display(ctx.MethodId)} -> typed-default {shape} {body.Length}b");
|
||||
@@ -25,6 +28,7 @@ internal sealed partial class GameRouter
|
||||
|
||||
private Task DefaultUnknownNotify4229938(RpcContext ctx)
|
||||
{
|
||||
UnknownMethodLogger.Log(ctx.Session.Log.Scope, "NOTIFY", ctx.MethodId, ctx.Body);
|
||||
// Notifies have no callback body. Accepting an unimplemented generated 4229938 notify is
|
||||
// safer than treating it as a protocol error; stateful methods get semantic handlers as
|
||||
// they are implemented, while telemetry/client-local signals remain harmless no-ops.
|
||||
@@ -35,7 +39,8 @@ internal sealed partial class GameRouter
|
||||
// High-frequency/UI RPCs seen in stock 4229938 are made explicit so they no longer appear as
|
||||
// unknown traffic. The server does not emulate production economy/social services here; it
|
||||
// returns the exact neutral value expected by the generated client callback.
|
||||
[Handler(MethodId.AskTradeGetMarketList, HandlerPacketKind.Invoke)]
|
||||
// AskTradeGetMarketList is handled for real by GameRouter.Trade.cs (synthesised market buckets);
|
||||
// it must not stay in this neutral list or registration reports a duplicate invoke handler.
|
||||
[Handler(MethodId.AskTradeGetHistoryPage, HandlerPacketKind.Invoke)]
|
||||
[Handler(MethodId.AskPlayerRankingSummary, HandlerPacketKind.Invoke)]
|
||||
[Handler(MethodId.AskPopularityPhoneFirstOpened, HandlerPacketKind.Invoke)]
|
||||
@@ -57,6 +62,96 @@ internal sealed partial class GameRouter
|
||||
}
|
||||
|
||||
[Handler(MethodId.OnParkourStateChange, HandlerPacketKind.Invoke)]
|
||||
private Task OnParkourStateChange4229938(Connection conn, UxRpcMessage msg)
|
||||
=> conn.ReturnEmptyOkAsync(msg);
|
||||
private async Task OnParkourStateChange4229938(Connection conn, UxRpcMessage msg)
|
||||
{
|
||||
var ctx = msg.Context;
|
||||
// ⛑ 哨兵用 uint? 而不是 0:ParkourState 10000「空状态」是合法值,0 会把它吞掉(硬规则 240)。
|
||||
uint? parkourState = null;
|
||||
var payload = msg.Body;
|
||||
if (payload != null && payload.Length >= 4)
|
||||
{
|
||||
parkourState = BitConverter.ToUInt32(payload, 0);
|
||||
conn.Log.Info($"[PARKOUR] state={parkourState} len={payload.Length} hex={Convert.ToHexString(payload)}");
|
||||
}
|
||||
|
||||
await conn.ReturnEmptyOkAsync(msg);
|
||||
|
||||
// 跑酷状态 → 临时能力门(严格跟状态:进才发、离开立刻撤)。未配置 = 整层跳过,零开销。
|
||||
if (parkourState is { } s && WebTraversal.HasParkourStateGates)
|
||||
await SyncParkourStateGates4229938(ctx, s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ★ <c>gameplay.webTraversal.parkourStateGates</c>:**严格跟状态**的临时能力门。
|
||||
///
|
||||
/// 客户端每上报一次 <c>OnParkourStateChange</c>(67897801) 就进一次,但只在
|
||||
/// <b>0→1 / 1→0 的边沿</b>各发一个 <c>SyncPaokuLimitChange</c>(68974941):
|
||||
/// 进入配置里的 ParkourState ⇒ <c>isAdd=true</c>;一旦离开 ⇒ 立刻 <c>isAdd=false</c> 撤回,
|
||||
/// <b>没有宽限期</b>(用户 2026-10-01 23:3x 定:大跳 5052 走这条,连跳 5040 走常驻)。
|
||||
///
|
||||
/// 当前配置:<c>"10081": [5052]</c> —— ParkourState 10081「八步赶蝉jump」即璎珑的虐杀原形跑酷
|
||||
/// (TOPIC §11.10:全量 604 份日志里只在下发 3040 门之后的两个会话出现过)。
|
||||
///
|
||||
/// ⛑ 高频 RPC(实测 ~1.2 次/秒),靠 <c>WorldEntryState.ActiveParkourGateStates</c> 去重,
|
||||
/// 否则每次上报都 add/remove 抖动。规则 238:这里不做 per-item 日志,只在边沿打一行。
|
||||
/// </summary>
|
||||
async Task SyncParkourStateGates4229938(RpcContext ctx, uint parkourState)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
var unitId = state.ActiveSpiritUnitId != 0 ? state.ActiveSpiritUnitId : Profile.InitialUnitId;
|
||||
if (unitId == 0)
|
||||
return;
|
||||
|
||||
var active = state.ActiveParkourGateStates;
|
||||
|
||||
// 换了单位(切角色)⇒ 旧单位上的门随之消失,记账重置;不补发 isAdd=false(旧单位已不在场)。
|
||||
if (state.ActiveParkourGateUnitId != unitId)
|
||||
{
|
||||
active.Clear();
|
||||
state.ActiveParkourGateUnitId = unitId;
|
||||
}
|
||||
|
||||
// ① 已挂上、但与当前 state 不符的:撤回。
|
||||
// ⛑ 只有 parkourStateGateRelease == "onLeave" 才撤。默认 "session" 模式跳过这一步 ——
|
||||
// 10081「八步赶蝉jump」是瞬时态(实机 add→remove 间隔 ~1s,同秒内还会来回),
|
||||
// 一撤就导致下一次触发前门已经没了(用户实测「第二次触发大跳就没了」)。
|
||||
if (WebTraversal.ReleaseParkourGatesOnLeave)
|
||||
{
|
||||
List<uint>? revoked = null;
|
||||
foreach (var prev in active)
|
||||
{
|
||||
if (prev == parkourState)
|
||||
continue;
|
||||
var prevIds = WebTraversal.ParkourStateGateIds(prev);
|
||||
if (prevIds.Count == 0)
|
||||
continue;
|
||||
await ctx.NotifyAsync(MethodId.IGameSceneToClient_SyncPaokuLimitChange,
|
||||
new SceneMethods.SyncPaokuLimitChange4229938
|
||||
{
|
||||
unitId = unitId,
|
||||
limitIds = prevIds.ToList(),
|
||||
isAdd = false,
|
||||
});
|
||||
(revoked ??= []).Add(prev);
|
||||
ctx.Session.Log.Info($"[PARKOUR-GATE] unit={unitId} state={prev} remove=[{string.Join(",", prevIds)}] nowState={parkourState}");
|
||||
}
|
||||
if (revoked != null)
|
||||
foreach (var prev in revoked)
|
||||
active.Remove(prev);
|
||||
}
|
||||
|
||||
// ② 当前 state 命中配置且还没挂上:下发(Add 返回 true 才是新边沿)
|
||||
var addIds = WebTraversal.ParkourStateGateIds(parkourState);
|
||||
if (addIds.Count > 0 && active.Add(parkourState))
|
||||
{
|
||||
await ctx.NotifyAsync(MethodId.IGameSceneToClient_SyncPaokuLimitChange,
|
||||
new SceneMethods.SyncPaokuLimitChange4229938
|
||||
{
|
||||
unitId = unitId,
|
||||
limitIds = addIds.ToList(),
|
||||
isAdd = true,
|
||||
});
|
||||
ctx.Session.Log.Info($"[PARKOUR-GATE] unit={unitId} state={parkourState} add=[{string.Join(",", addIds)}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user