Initial commit: Ananta CBT 4229938 private server with all fixes
This commit is contained in:
commit
c84135c357
286 files changed
+2214315
No files matched your search
@@ -0,0 +1,180 @@
|
||||
using Ananta.SDK.Rpc;
|
||||
using Ananta.Server.Handlers;
|
||||
using Ananta.Server.Gameplay;
|
||||
using Ananta.Server.Protocol.Client4229938;
|
||||
|
||||
namespace Ananta.Server.Handlers.Game;
|
||||
|
||||
/// <summary>Protagonist traversal/web buff handling. Buff ids come from private-server.json.</summary>
|
||||
internal sealed partial class GameRouter
|
||||
{
|
||||
static bool IsActiveProtagonist(WorldEntryState state, ulong unitId = 0)
|
||||
=> WebTraversal.IsProtagonist(state.ActiveSpiritTemplateId)
|
||||
&& (unitId == 0 || unitId == state.ActiveSpiritUnitId);
|
||||
|
||||
async Task EnsureFeiSuoActionBuff(RpcContext ctx)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (!IsActiveProtagonist(state))
|
||||
return;
|
||||
if (state.ActiveFeiSuoBuffInstanceId != 0)
|
||||
return;
|
||||
|
||||
var instanceId = state.NextBuffInstanceId++;
|
||||
state.ActiveFeiSuoBuffInstanceId = instanceId;
|
||||
state.ActiveFeiSuoBuffUnitId = state.ActiveSpiritUnitId;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitAddBuff,
|
||||
WorldCodec.UnitAddBuff(state.ActiveSpiritUnitId, WebTraversal.FeiSuoBuff, instanceId));
|
||||
}
|
||||
|
||||
async Task OnEnterFeiSuoCrouch(RpcContext ctx)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (!IsActiveProtagonist(state))
|
||||
{
|
||||
ctx.Session.Log.Info($"[WEB] feisuo-enter ignored template={state.ActiveSpiritTemplateId} protagonist=false");
|
||||
return;
|
||||
}
|
||||
await PublishInitialCapabilityBuffs(ctx);
|
||||
await EnsureFeiSuoActionBuff(ctx);
|
||||
ctx.Session.Log.Info($"[WEB] feisuo-enter ack=true transient={WebTraversal.FeiSuoBuff}");
|
||||
}
|
||||
|
||||
async Task OnFeiSuoSuccess(RpcContext ctx, int feiSuoId)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (!IsActiveProtagonist(state))
|
||||
{
|
||||
ctx.Session.Log.Info($"[WEB] feisuo-success ignored template={state.ActiveSpiritTemplateId} protagonist=false");
|
||||
return;
|
||||
}
|
||||
await EnsureFeiSuoActionBuff(ctx);
|
||||
ctx.Session.Log.Info($"[WEB] feisuo-success id={feiSuoId} transient={WebTraversal.FeiSuoBuff}");
|
||||
}
|
||||
|
||||
async Task OnLeaveFeiSuoCrouch(RpcContext ctx)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (state.ActiveFeiSuoBuffInstanceId == 0)
|
||||
return;
|
||||
|
||||
var instanceId = state.ActiveFeiSuoBuffInstanceId;
|
||||
var unitId = state.ActiveFeiSuoBuffUnitId != 0
|
||||
? state.ActiveFeiSuoBuffUnitId
|
||||
: state.ActiveSpiritUnitId;
|
||||
state.ActiveFeiSuoBuffInstanceId = 0;
|
||||
state.ActiveFeiSuoBuffUnitId = 0;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitRemoveBuff,
|
||||
WorldCodec.UnitRemoveBuff(unitId, instanceId));
|
||||
ctx.Session.Log.Info($"[WEB] feisuo-leave transient-removed instance={instanceId}");
|
||||
}
|
||||
|
||||
async Task OnClientBuffAdd(RpcContext ctx, ulong unitId, uint buffId)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (WebTraversal.IsWebBuff(buffId) && !IsActiveProtagonist(state, unitId))
|
||||
{
|
||||
ctx.Session.Log.Info($"[WEB] client-buff-add denied unit={unitId} template={state.ActiveSpiritTemplateId} buff={buffId} protagonist=false");
|
||||
return;
|
||||
}
|
||||
// SwingBuff/WallRushBuff are action states, not unlocks. The 4229938 client requests them
|
||||
// when an action starts and removes them when it ends. Track one exact instance per unit/buff
|
||||
// so repeated client requests refresh the state instead of stacking permanent super-armor.
|
||||
if (WebTraversal.IsClientLifecycleBuff(buffId))
|
||||
{
|
||||
var key = (unitId, buffId);
|
||||
if (state.ActiveClientWebBuffInstances.Remove(key, out var previousInstanceId))
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitRemoveBuff, WorldCodec.UnitRemoveBuff(unitId, previousInstanceId));
|
||||
|
||||
var lifecycleInstanceId = state.NextBuffInstanceId++;
|
||||
state.ActiveClientWebBuffInstances[key] = lifecycleInstanceId;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitAddBuff,
|
||||
WorldCodec.UnitAddBuff(unitId, buffId, lifecycleInstanceId));
|
||||
ctx.Session.Log.Info($"[WEB] lifecycle-add unit={unitId} buff={buffId} instance={lifecycleInstanceId} refreshed={previousInstanceId != 0}");
|
||||
return;
|
||||
}
|
||||
|
||||
var instanceId = state.NextBuffInstanceId++;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitAddBuff, WorldCodec.UnitAddBuff(unitId, buffId, instanceId));
|
||||
if (buffId == WebTraversal.FeiSuoBuff)
|
||||
{
|
||||
state.ActiveFeiSuoBuffInstanceId = instanceId;
|
||||
state.ActiveFeiSuoBuffUnitId = unitId;
|
||||
}
|
||||
|
||||
if (buffId == WebTraversal.FeiSuoBuff || WebTraversal.IsProtected(buffId))
|
||||
ctx.Session.Log.Info($"[WEB] client-buff-add unit={unitId} buff={buffId} instance={instanceId}");
|
||||
}
|
||||
|
||||
async Task OnClientBuffRemove(RpcContext ctx, ulong unitId, uint buffId)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
if (WebTraversal.IsWebBuff(buffId) && !IsActiveProtagonist(state, unitId))
|
||||
return;
|
||||
if (WebTraversal.IsClientLifecycleBuff(buffId))
|
||||
{
|
||||
var key = (unitId, buffId);
|
||||
if (state.ActiveClientWebBuffInstances.Remove(key, out var lifecycleInstanceId))
|
||||
{
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitRemoveBuff,
|
||||
WorldCodec.UnitRemoveBuff(unitId, lifecycleInstanceId));
|
||||
ctx.Session.Log.Info($"[WEB] lifecycle-remove unit={unitId} buff={buffId} instance={lifecycleInstanceId}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffId == WebTraversal.FeiSuoBuff)
|
||||
{
|
||||
if (state.ActiveFeiSuoBuffInstanceId != 0)
|
||||
{
|
||||
var instanceId = state.ActiveFeiSuoBuffInstanceId;
|
||||
var transientUnitId = state.ActiveFeiSuoBuffUnitId != 0
|
||||
? state.ActiveFeiSuoBuffUnitId
|
||||
: unitId;
|
||||
state.ActiveFeiSuoBuffInstanceId = 0;
|
||||
state.ActiveFeiSuoBuffUnitId = 0;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitRemoveBuff,
|
||||
WorldCodec.UnitRemoveBuff(transientUnitId, instanceId));
|
||||
ctx.Session.Log.Info($"[WEB] client-buff-remove transient={buffId} instance={instanceId}");
|
||||
}
|
||||
|
||||
// Client 4229938 reports grapple teardown by buff *id*, not by instance.
|
||||
// Locally that teardown can consume the configured resident grapple gate as well as the short-lived
|
||||
// action copy. This is especially visible on the female protagonist: the first grapple
|
||||
// succeeds, then no second client-buff-add is emitted. Re-arm one untracked resident gate
|
||||
// after teardown for either protagonist. It is deliberately NOT stored as
|
||||
// ActiveFeiSuoBuffInstanceId; that slot is reserved for the next action copy only.
|
||||
await Task.Delay(WebTraversal.GrappleRearmDelayMs);
|
||||
if (IsActiveProtagonist(state, unitId))
|
||||
{
|
||||
var residentInstanceId = state.NextBuffInstanceId++;
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitAddBuff,
|
||||
WorldCodec.UnitAddBuff(unitId, WebTraversal.FeiSuoBuff, residentInstanceId));
|
||||
ctx.Session.Log.Info($"[WEB] persistent-gate-reassert unit={unitId} buff={WebTraversal.FeiSuoBuff} instance={residentInstanceId}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WebTraversal.IsProtected(buffId))
|
||||
return;
|
||||
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitAddBuff, WorldCodec.UnitAddBuff(unitId, buffId, state.NextBuffInstanceId++));
|
||||
ctx.Session.Log.Info($"[WEB] protected-buff-reassert unit={unitId} buff={buffId}");
|
||||
}
|
||||
async Task ClearClientWebLifecycleBuffs(RpcContext ctx, ulong unitId, string phase)
|
||||
{
|
||||
var state = GetWorldState(ctx);
|
||||
var active = state.ActiveClientWebBuffInstances
|
||||
.Where(x => x.Key.UnitId == unitId)
|
||||
.ToArray();
|
||||
foreach (var item in active)
|
||||
{
|
||||
state.ActiveClientWebBuffInstances.Remove(item.Key);
|
||||
await ctx.NotifyAsync(MethodId.SyncUnitRemoveBuff,
|
||||
WorldCodec.UnitRemoveBuff(unitId, item.Value));
|
||||
}
|
||||
if (active.Length > 0)
|
||||
ctx.Session.Log.Info($"[WEB] lifecycle-clear phase={phase} unit={unitId} count={active.Length}");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user