using Yuanshen.Gameserver.Controllers.Attributes; using Yuanshen.Gameserver.Controllers.Result; using Yuanshen.Gameserver.Game; using Yuanshen.Gameserver.Network.Session; using Yuanshen.Protocol; using Google.Protobuf; using Microsoft.Extensions.Logging; namespace Yuanshen.Gameserver.Controllers; [NetController] internal sealed class CombatController : ControllerBase { [NetCommand(CmdType.UnionCmdNotify)] public async ValueTask OnUnionCmdNotify(GameState state, Player player, NetSession session, CombatManager combat, WorldSpawner worldSpawner, ILogger logger) { UnionCmdNotify notify = Packet!.DecodeBody(); foreach (UnionCmd cmd in notify.CmdList) { if (cmd.MessageId == (uint)CmdType.CombatInvocationsNotify) await ProcessCombatInvocationsAsync(CombatInvocationsNotify.Parser.ParseFrom(cmd.Body), state, player, combat, worldSpawner, logger, session); else if (cmd.MessageId == (uint)CmdType.AbilityInvocationsNotify) await AbilityController.ForwardAsync(session, AbilityInvocationsNotify.Parser.ParseFrom(cmd.Body).Invokes); } return Ok(); } [NetCommand(CmdType.CombatInvocationsNotify)] public async ValueTask OnCombatInvocationsNotify(GameState state, Player player, CombatManager combat, WorldSpawner worldSpawner, ILogger logger, NetSession session) { await ProcessCombatInvocationsAsync(Packet!.DecodeBody(), state, player, combat, worldSpawner, logger, session); return Ok(); } [NetCommand(CmdType.EvtBeingHitNotify)] public async ValueTask OnEvtBeingHitNotify(CombatManager combat) { EvtBeingHitNotify notify = Packet!.DecodeBody(); if (notify.BeingHitInfo?.AttackResult is not null) await combat.HandleAttackAsync(notify.BeingHitInfo.AttackResult); return Ok(); } [NetCommand(CmdType.EvtBeingHitsCombineNotify)] public async ValueTask OnEvtBeingHitsCombineNotify(CombatManager combat) { EvtBeingHitsCombineNotify notify = Packet!.DecodeBody(); foreach (EvtBeingHitInfo hit in notify.EvtBeingHitInfoList) { if (hit.AttackResult is not null) await combat.HandleAttackAsync(hit.AttackResult); } return Ok(); } private static async ValueTask ProcessCombatInvocationsAsync(CombatInvocationsNotify notify, GameState state, Player player, CombatManager combat, WorldSpawner worldSpawner, ILogger logger, NetSession session) { uint activeEntityId = player.ActiveAvatar?.EntityId ?? 0; CombatInvocationsNotify forward = new(); foreach (CombatInvokeEntry entry in notify.InvokeList) { switch (entry.ArgumentType) { case CombatTypeArgument.EntityMove: EntityMoveInfo move = EntityMoveInfo.Parser.ParseFrom(entry.CombatData); if (move.EntityId == activeEntityId && move.MotionInfo?.Pos is not null) { state.UpdatePosition(move.MotionInfo.Pos); await worldSpawner.StreamAsync(move.MotionInfo.Pos); } break; case CombatTypeArgument.CombatEvtBeingHit: EvtBeingHitInfo hit = EvtBeingHitInfo.Parser.ParseFrom(entry.CombatData); logger.LogInformation("HIT defense={d} damage={dmg} forward={f}", hit.AttackResult?.DefenseId, hit.AttackResult?.Damage, entry.ForwardType); if (hit.AttackResult is not null) await combat.HandleAttackAsync(hit.AttackResult); break; default: logger.LogDebug("combat arg={arg} fwd={f}", entry.ArgumentType, entry.ForwardType); break; } if (entry.ForwardType is ForwardType.ForwardToAll or ForwardType.ForwardToHost or ForwardType.ForwardToPeer) forward.InvokeList.Add(entry); } if (forward.InvokeList.Count != 0) await session.NotifyAsync(CmdType.CombatInvocationsNotify, forward); } }