From 42cca1e786a6c89ede51804cbe2ed1f9fa21ddc2 Mon Sep 17 00:00:00 2001 From: thexeondev <149735250+thexeondev@users.noreply.github.com> Date: Fri, 5 Jan 2024 01:58:31 +0300 Subject: [PATCH] Handle movement, no cooldowns for skills (reset immediately) --- .../Dispatching/NetCommandDispatcher.cs | 3 +- .../Controllers/SceneController.cs | 41 ++++++++++++++++++- .../Game/Entity/EntityManager.cs | 12 ++++++ .../Entity/Listener/IEntityEventListener.cs | 1 + .../Game/Entity/SceneEntity.cs | 12 ++++++ NahidaImpact.Gameserver/Game/Player.cs | 2 + .../Game/Scene/ClientActionManager.cs | 20 +++++++++ .../Game/Scene/SceneManager.cs | 31 +++++++++++++- .../Helpers/UnionCmdHelper.cs | 25 +++++++++++ NahidaImpact.Gameserver/Network/NetPacket.cs | 4 +- .../Network/Session/NetSession.cs | 36 +++++++++------- .../Session/SessionEntityEventListener.cs | 12 +++++- NahidaImpact.Gameserver/Program.cs | 1 + 13 files changed, 179 insertions(+), 21 deletions(-) create mode 100644 NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs create mode 100644 NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs diff --git a/NahidaImpact.Gameserver/Controllers/Dispatching/NetCommandDispatcher.cs b/NahidaImpact.Gameserver/Controllers/Dispatching/NetCommandDispatcher.cs index b339821..c975e3c 100644 --- a/NahidaImpact.Gameserver/Controllers/Dispatching/NetCommandDispatcher.cs +++ b/NahidaImpact.Gameserver/Controllers/Dispatching/NetCommandDispatcher.cs @@ -7,6 +7,7 @@ using NahidaImpact.Gameserver.Network; using NahidaImpact.Protocol; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System.Diagnostics; namespace NahidaImpact.Gameserver.Controllers.Dispatching; internal class NetCommandDispatcher(IServiceProvider serviceProvider, ILogger logger) @@ -29,7 +30,7 @@ internal class NetCommandDispatcher(IServiceProvider serviceProvider, ILogger OnEvtDoSkillSuccNotify(SceneManager sceneManager) + { + EvtDoSkillSuccNotify notify = Packet!.DecodeBody(); + await sceneManager.ResetAllCoolDownsForAvatar(notify.CasterId); + + return Ok(); + } + + [NetCommand(CmdType.CombatInvocationsNotify)] + public async ValueTask OnCombatInvocationsNotify(ClientActionManager clientActionManager) + { + CombatInvocationsNotify notify = Packet!.DecodeBody(); + foreach (CombatInvokeEntry invocation in notify.InvokeList) + { + IMessage? message = invocation.DecodeCombatInvocation(); + if (message != null) + await clientActionManager.InvokeAsync(invocation.ArgumentType, message); + } + + return Ok(); + } + + [NetCommand(CmdType.UnionCmdNotify)] + public async ValueTask OnUnionCmdNotify(NetSession session) + { + UnionCmdNotify notify = Packet!.DecodeBody(); + + foreach (UnionCmd cmd in notify.CmdList) + { + await session.HandlePacket(cmd.ToNetPacket()); + } + + return Ok(); + } + [NetCommand(CmdType.GetScenePointReq)] public ValueTask OnGetScenePointReq(SceneManager sceneManager, Player player) { diff --git a/NahidaImpact.Gameserver/Game/Entity/EntityManager.cs b/NahidaImpact.Gameserver/Game/Entity/EntityManager.cs index f4a9476..9164563 100644 --- a/NahidaImpact.Gameserver/Game/Entity/EntityManager.cs +++ b/NahidaImpact.Gameserver/Game/Entity/EntityManager.cs @@ -13,6 +13,18 @@ internal class EntityManager(IEntityEventListener listener) await _listener.OnEntitySpawned(entity, visionType); } + public async ValueTask ChangeAvatarFightPropAsync(uint entityId, uint key, float value) + { + if (GetEntityById(entityId) is AvatarEntity entity) + { + entity.SetFightProp(key, value); + await _listener.OnAvatarFightPropChanged(entity, key, value); + } + } + + public SceneEntity? GetEntityById(uint id) => + _entities.Find(entity => entity.EntityId == id); + public void Reset() { _entities.Clear(); diff --git a/NahidaImpact.Gameserver/Game/Entity/Listener/IEntityEventListener.cs b/NahidaImpact.Gameserver/Game/Entity/Listener/IEntityEventListener.cs index 9506898..bb9544c 100644 --- a/NahidaImpact.Gameserver/Game/Entity/Listener/IEntityEventListener.cs +++ b/NahidaImpact.Gameserver/Game/Entity/Listener/IEntityEventListener.cs @@ -4,4 +4,5 @@ namespace NahidaImpact.Gameserver.Game.Entity.Listener; internal interface IEntityEventListener { ValueTask OnEntitySpawned(SceneEntity entity, VisionType visionType); + ValueTask OnAvatarFightPropChanged(AvatarEntity entity, uint key, float value); } diff --git a/NahidaImpact.Gameserver/Game/Entity/SceneEntity.cs b/NahidaImpact.Gameserver/Game/Entity/SceneEntity.cs index 685854c..862b167 100644 --- a/NahidaImpact.Gameserver/Game/Entity/SceneEntity.cs +++ b/NahidaImpact.Gameserver/Game/Entity/SceneEntity.cs @@ -33,6 +33,18 @@ internal abstract class SceneEntity MotionInfo.Rot.Z = z; } + public void SetFightProp(uint key, float value) + { + FightPropPair? pair = FightProperties.Find(p => p.PropType == key); + if (pair == null) + { + pair = new() { PropType = key }; + FightProperties.Add(pair); + } + + pair.PropValue = value; + } + public virtual SceneEntityInfo AsInfo() { SceneEntityInfo info = new() diff --git a/NahidaImpact.Gameserver/Game/Player.cs b/NahidaImpact.Gameserver/Game/Player.cs index ac18043..a33d5c2 100644 --- a/NahidaImpact.Gameserver/Game/Player.cs +++ b/NahidaImpact.Gameserver/Game/Player.cs @@ -15,6 +15,8 @@ internal class Player(ExcelTableCollection excelTables) public List AvatarTeams { get; set; } = []; public uint CurTeamIndex { get; set; } + public uint CurAvatarEntityId { get; set; } + private readonly ExcelTableCollection _excelTables = excelTables; public void InitDefaultPlayer() diff --git a/NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs b/NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs new file mode 100644 index 0000000..4617705 --- /dev/null +++ b/NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; + +namespace NahidaImpact.Gameserver.Game.Scene; +internal class ClientActionManager(SceneManager sceneManager) +{ + public async ValueTask InvokeAsync(CombatTypeArgument combatTypeArgument, IMessage data) + { + switch (combatTypeArgument) + { + case CombatTypeArgument.EntityMove: + await PerformEntityMovement((EntityMoveInfo)data); + break; + } + } + + public async ValueTask PerformEntityMovement(EntityMoveInfo info) + { + await sceneManager.MotionChanged(info.EntityId, info.MotionInfo); + } +} diff --git a/NahidaImpact.Gameserver/Game/Scene/SceneManager.cs b/NahidaImpact.Gameserver/Game/Scene/SceneManager.cs index 4f77742..d05f6ab 100644 --- a/NahidaImpact.Gameserver/Game/Scene/SceneManager.cs +++ b/NahidaImpact.Gameserver/Game/Scene/SceneManager.cs @@ -1,4 +1,5 @@ using System.Numerics; +using NahidaImpact.Common.Constants; using NahidaImpact.Common.Data.Binout; using NahidaImpact.Gameserver.Controllers; using NahidaImpact.Gameserver.Game.Avatar; @@ -60,13 +61,35 @@ internal class SceneManager(NetSession session, Player player, EntityManager ent _enterState = SceneEnterState.Complete; } + public async ValueTask ResetAllCoolDownsForAvatar(uint entityId) + { + await _entityManager.ChangeAvatarFightPropAsync(entityId, FightProp.FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO, 1); + await _entityManager.ChangeAvatarFightPropAsync(entityId, FightProp.FIGHT_PROP_SKILL_CD_MINUS_RATIO, 1); + } + + public ValueTask MotionChanged(uint entityId, MotionInfo motion) + { + SceneEntity? entity = _entityManager.GetEntityById(entityId); + if (entity == null) return ValueTask.CompletedTask; + + entity.MotionInfo = motion; + // need to notify peers, but it's single player anyway (for now) + + return ValueTask.CompletedTask; + } + public async ValueTask ReplaceCurrentAvatarAsync(ulong replaceToGuid) { // TODO: add logic checks. + SceneEntity previousEntity = _entityManager.GetEntityById(_player.CurAvatarEntityId)!; + AvatarEntity avatar = _teamAvatars.Find(a => a.GameAvatar.Guid == replaceToGuid) ?? throw new ArgumentException($"ReplaceCurrentAvatar: avatar with guid {replaceToGuid} not in team!"); + avatar.MotionInfo = previousEntity.MotionInfo; // maybe make a better way to do this? + + _player.CurAvatarEntityId = avatar.EntityId; await _entityManager.SpawnEntityAsync(avatar, VisionType.Replace); } @@ -74,22 +97,28 @@ internal class SceneManager(NetSession session, Player player, EntityManager ent { _teamAvatars.Clear(); + SceneEntity previousEntity = _entityManager.GetEntityById(_player.CurAvatarEntityId)!; + Vector pos = previousEntity.MotionInfo.Pos; + foreach (ulong guid in guidList) { GameAvatar gameAvatar = _player.Avatars.Find(avatar => avatar.Guid == guid)!; AvatarEntity avatarEntity = _entityFactory.CreateAvatar(gameAvatar, _player.Uid); - avatarEntity.SetPosition(2336.789f, 249.98896f, -751.3081f); + avatarEntity.SetPosition(pos.X, pos.Y, pos.Z); _teamAvatars.Add(avatarEntity); } await SendSceneTeamUpdate(); + + _player.CurAvatarEntityId = _teamAvatars[0].EntityId; await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born); } private async ValueTask OnEnterDone() { + _player.CurAvatarEntityId = _teamAvatars[0].EntityId; await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born); } diff --git a/NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs b/NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs new file mode 100644 index 0000000..8021889 --- /dev/null +++ b/NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs @@ -0,0 +1,25 @@ +using Google.Protobuf; +using NahidaImpact.Gameserver.Network; +using NahidaImpact.Protocol; + +namespace NahidaImpact.Gameserver.Helpers; +internal static class UnionCmdHelper +{ + public static IMessage? DecodeCombatInvocation(this CombatInvokeEntry invocation) + { + return invocation.ArgumentType switch + { + CombatTypeArgument.EntityMove => EntityMoveInfo.Parser.ParseFrom(invocation.CombatData), + _ => null + }; + } + + public static NetPacket ToNetPacket(this UnionCmd cmd) + { + return new NetPacket() + { + CmdType = (CmdType)cmd.MessageId, + Body = cmd.Body.Memory + }; + } +} diff --git a/NahidaImpact.Gameserver/Network/NetPacket.cs b/NahidaImpact.Gameserver/Network/NetPacket.cs index d7ebb7e..bde42ee 100644 --- a/NahidaImpact.Gameserver/Network/NetPacket.cs +++ b/NahidaImpact.Gameserver/Network/NetPacket.cs @@ -9,8 +9,8 @@ internal class NetPacket private const ushort TailMagic = 0x89AB; public CmdType CmdType { get; set; } - public Memory Head { get; set; } - public Memory Body { get; set; } + public ReadOnlyMemory Head { get; set; } + public ReadOnlyMemory Body { get; set; } public TBody DecodeBody() where TBody : IMessage, new() { diff --git a/NahidaImpact.Gameserver/Network/Session/NetSession.cs b/NahidaImpact.Gameserver/Network/Session/NetSession.cs index 7e7d9ea..432a630 100644 --- a/NahidaImpact.Gameserver/Network/Session/NetSession.cs +++ b/NahidaImpact.Gameserver/Network/Session/NetSession.cs @@ -5,6 +5,7 @@ using NahidaImpact.Gameserver.Controllers.Result; using NahidaImpact.Protocol; using Google.Protobuf; using Microsoft.Extensions.Logging; +using System.Diagnostics; namespace NahidaImpact.Gameserver.Network.Session; internal abstract class NetSession(ILogger logger, NetSessionManager sessionManager, NetCommandDispatcher commandDispatcher) : IDisposable @@ -41,6 +42,25 @@ internal abstract class NetSession(ILogger logger, NetSessionManager }); } + public async ValueTask HandlePacket(NetPacket packet) + { + IResult? result = await _commandDispatcher.InvokeHandler(packet); + if (result != null) + { + while (result.NextPacket(out NetPacket? serverPacket)) + { + await SendAsync(serverPacket); + + if (serverPacket.CmdType == CmdType.GetPlayerTokenRsp) + { + InitializeEncryption(1337); // hardcoded MT seed with patch + } + } + + Debug.WriteLine("Successfully handled command of type {0}", packet.CmdType); + } + } + protected async ValueTask ConsumePacketsAsync(Memory buffer) { if (buffer.Length < 12) @@ -55,21 +75,7 @@ internal abstract class NetSession(ILogger logger, NetSessionManager if (packet == null) return consumed; - IResult? result = await _commandDispatcher.InvokeHandler(packet); - if (result != null) - { - while (result.NextPacket(out NetPacket? serverPacket)) - { - await SendAsync(serverPacket); - - if (serverPacket.CmdType == CmdType.GetPlayerTokenRsp) - { - InitializeEncryption(1337); // hardcoded MT seed with patch - } - } - - _logger.LogInformation("Successfully handled command of type {cmdType}", packet.CmdType); - } + if (packet != null) await HandlePacket(packet); } while (buffer.Length - consumed >= 12); return consumed; diff --git a/NahidaImpact.Gameserver/Network/Session/SessionEntityEventListener.cs b/NahidaImpact.Gameserver/Network/Session/SessionEntityEventListener.cs index b541099..c3d7887 100644 --- a/NahidaImpact.Gameserver/Network/Session/SessionEntityEventListener.cs +++ b/NahidaImpact.Gameserver/Network/Session/SessionEntityEventListener.cs @@ -1,4 +1,5 @@ -using NahidaImpact.Gameserver.Game.Entity; +using NahidaImpact.Common.Constants; +using NahidaImpact.Gameserver.Game.Entity; using NahidaImpact.Gameserver.Game.Entity.Listener; using NahidaImpact.Protocol; @@ -7,6 +8,15 @@ internal class SessionEntityEventListener(NetSession session) : IEntityEventList { private readonly NetSession _session = session; + public async ValueTask OnAvatarFightPropChanged(AvatarEntity entity, uint key, float value) + { + await _session.NotifyAsync(CmdType.AvatarFightPropUpdateNotify, new AvatarFightPropUpdateNotify + { + AvatarGuid = entity.GameAvatar.Guid, + FightPropMap = {{key, value}} + }); + } + public async ValueTask OnEntitySpawned(SceneEntity entity, VisionType visionType) { await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, new SceneEntityAppearNotify diff --git a/NahidaImpact.Gameserver/Program.cs b/NahidaImpact.Gameserver/Program.cs index 6790982..6f50d5f 100644 --- a/NahidaImpact.Gameserver/Program.cs +++ b/NahidaImpact.Gameserver/Program.cs @@ -39,6 +39,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Logic Listeners builder.Services.AddScoped();