Handle movement, no cooldowns for skills (reset immediately)

This commit is contained in:
thexeondev 2024-01-05 01:58:31 +03:00
parent a762ec8241
commit 42cca1e786
13 changed files with 179 additions and 21 deletions

View file

@ -7,6 +7,7 @@ using NahidaImpact.Gameserver.Network;
using NahidaImpact.Protocol; using NahidaImpact.Protocol;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace NahidaImpact.Gameserver.Controllers.Dispatching; namespace NahidaImpact.Gameserver.Controllers.Dispatching;
internal class NetCommandDispatcher(IServiceProvider serviceProvider, ILogger<NetCommandDispatcher> logger) internal class NetCommandDispatcher(IServiceProvider serviceProvider, ILogger<NetCommandDispatcher> logger)
@ -29,7 +30,7 @@ internal class NetCommandDispatcher(IServiceProvider serviceProvider, ILogger<Ne
return await handler(_serviceProvider, packet); return await handler(_serviceProvider, packet);
} }
_logger.LogWarning("No handler defined for command of type {cmdType}", packet.CmdType); Debug.WriteLine("No handler defined for command of type {0}", packet.CmdType);
return null; return null;
} }

View file

@ -1,7 +1,10 @@
using NahidaImpact.Gameserver.Controllers.Attributes; using Google.Protobuf;
using NahidaImpact.Gameserver.Controllers.Attributes;
using NahidaImpact.Gameserver.Controllers.Result; using NahidaImpact.Gameserver.Controllers.Result;
using NahidaImpact.Gameserver.Game; using NahidaImpact.Gameserver.Game;
using NahidaImpact.Gameserver.Game.Scene; using NahidaImpact.Gameserver.Game.Scene;
using NahidaImpact.Gameserver.Helpers;
using NahidaImpact.Gameserver.Network.Session;
using NahidaImpact.Protocol; using NahidaImpact.Protocol;
namespace NahidaImpact.Gameserver.Controllers; namespace NahidaImpact.Gameserver.Controllers;
@ -9,6 +12,42 @@ namespace NahidaImpact.Gameserver.Controllers;
[NetController] [NetController]
internal class SceneController : ControllerBase internal class SceneController : ControllerBase
{ {
[NetCommand(CmdType.EvtDoSkillSuccNotify)]
public async ValueTask<IResult> OnEvtDoSkillSuccNotify(SceneManager sceneManager)
{
EvtDoSkillSuccNotify notify = Packet!.DecodeBody<EvtDoSkillSuccNotify>();
await sceneManager.ResetAllCoolDownsForAvatar(notify.CasterId);
return Ok();
}
[NetCommand(CmdType.CombatInvocationsNotify)]
public async ValueTask<IResult> OnCombatInvocationsNotify(ClientActionManager clientActionManager)
{
CombatInvocationsNotify notify = Packet!.DecodeBody<CombatInvocationsNotify>();
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<IResult> OnUnionCmdNotify(NetSession session)
{
UnionCmdNotify notify = Packet!.DecodeBody<UnionCmdNotify>();
foreach (UnionCmd cmd in notify.CmdList)
{
await session.HandlePacket(cmd.ToNetPacket());
}
return Ok();
}
[NetCommand(CmdType.GetScenePointReq)] [NetCommand(CmdType.GetScenePointReq)]
public ValueTask<IResult> OnGetScenePointReq(SceneManager sceneManager, Player player) public ValueTask<IResult> OnGetScenePointReq(SceneManager sceneManager, Player player)
{ {

View file

@ -13,6 +13,18 @@ internal class EntityManager(IEntityEventListener listener)
await _listener.OnEntitySpawned(entity, visionType); 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() public void Reset()
{ {
_entities.Clear(); _entities.Clear();

View file

@ -4,4 +4,5 @@ namespace NahidaImpact.Gameserver.Game.Entity.Listener;
internal interface IEntityEventListener internal interface IEntityEventListener
{ {
ValueTask OnEntitySpawned(SceneEntity entity, VisionType visionType); ValueTask OnEntitySpawned(SceneEntity entity, VisionType visionType);
ValueTask OnAvatarFightPropChanged(AvatarEntity entity, uint key, float value);
} }

View file

@ -33,6 +33,18 @@ internal abstract class SceneEntity
MotionInfo.Rot.Z = z; 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() public virtual SceneEntityInfo AsInfo()
{ {
SceneEntityInfo info = new() SceneEntityInfo info = new()

View file

@ -15,6 +15,8 @@ internal class Player(ExcelTableCollection excelTables)
public List<GameAvatarTeam> AvatarTeams { get; set; } = []; public List<GameAvatarTeam> AvatarTeams { get; set; } = [];
public uint CurTeamIndex { get; set; } public uint CurTeamIndex { get; set; }
public uint CurAvatarEntityId { get; set; }
private readonly ExcelTableCollection _excelTables = excelTables; private readonly ExcelTableCollection _excelTables = excelTables;
public void InitDefaultPlayer() public void InitDefaultPlayer()

View file

@ -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);
}
}

View file

@ -1,4 +1,5 @@
using System.Numerics; using System.Numerics;
using NahidaImpact.Common.Constants;
using NahidaImpact.Common.Data.Binout; using NahidaImpact.Common.Data.Binout;
using NahidaImpact.Gameserver.Controllers; using NahidaImpact.Gameserver.Controllers;
using NahidaImpact.Gameserver.Game.Avatar; using NahidaImpact.Gameserver.Game.Avatar;
@ -60,13 +61,35 @@ internal class SceneManager(NetSession session, Player player, EntityManager ent
_enterState = SceneEnterState.Complete; _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) public async ValueTask ReplaceCurrentAvatarAsync(ulong replaceToGuid)
{ {
// TODO: add logic checks. // TODO: add logic checks.
SceneEntity previousEntity = _entityManager.GetEntityById(_player.CurAvatarEntityId)!;
AvatarEntity avatar = _teamAvatars.Find(a => a.GameAvatar.Guid == replaceToGuid) AvatarEntity avatar = _teamAvatars.Find(a => a.GameAvatar.Guid == replaceToGuid)
?? throw new ArgumentException($"ReplaceCurrentAvatar: avatar with guid {replaceToGuid} not in team!"); ?? 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); await _entityManager.SpawnEntityAsync(avatar, VisionType.Replace);
} }
@ -74,22 +97,28 @@ internal class SceneManager(NetSession session, Player player, EntityManager ent
{ {
_teamAvatars.Clear(); _teamAvatars.Clear();
SceneEntity previousEntity = _entityManager.GetEntityById(_player.CurAvatarEntityId)!;
Vector pos = previousEntity.MotionInfo.Pos;
foreach (ulong guid in guidList) foreach (ulong guid in guidList)
{ {
GameAvatar gameAvatar = _player.Avatars.Find(avatar => avatar.Guid == guid)!; GameAvatar gameAvatar = _player.Avatars.Find(avatar => avatar.Guid == guid)!;
AvatarEntity avatarEntity = _entityFactory.CreateAvatar(gameAvatar, _player.Uid); 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); _teamAvatars.Add(avatarEntity);
} }
await SendSceneTeamUpdate(); await SendSceneTeamUpdate();
_player.CurAvatarEntityId = _teamAvatars[0].EntityId;
await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born); await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born);
} }
private async ValueTask OnEnterDone() private async ValueTask OnEnterDone()
{ {
_player.CurAvatarEntityId = _teamAvatars[0].EntityId;
await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born); await _entityManager.SpawnEntityAsync(_teamAvatars[0], VisionType.Born);
} }

View file

@ -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
};
}
}

View file

@ -9,8 +9,8 @@ internal class NetPacket
private const ushort TailMagic = 0x89AB; private const ushort TailMagic = 0x89AB;
public CmdType CmdType { get; set; } public CmdType CmdType { get; set; }
public Memory<byte> Head { get; set; } public ReadOnlyMemory<byte> Head { get; set; }
public Memory<byte> Body { get; set; } public ReadOnlyMemory<byte> Body { get; set; }
public TBody DecodeBody<TBody>() where TBody : IMessage<TBody>, new() public TBody DecodeBody<TBody>() where TBody : IMessage<TBody>, new()
{ {

View file

@ -5,6 +5,7 @@ using NahidaImpact.Gameserver.Controllers.Result;
using NahidaImpact.Protocol; using NahidaImpact.Protocol;
using Google.Protobuf; using Google.Protobuf;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace NahidaImpact.Gameserver.Network.Session; namespace NahidaImpact.Gameserver.Network.Session;
internal abstract class NetSession(ILogger<NetSession> logger, NetSessionManager sessionManager, NetCommandDispatcher commandDispatcher) : IDisposable internal abstract class NetSession(ILogger<NetSession> logger, NetSessionManager sessionManager, NetCommandDispatcher commandDispatcher) : IDisposable
@ -41,6 +42,25 @@ internal abstract class NetSession(ILogger<NetSession> 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<int> ConsumePacketsAsync(Memory<byte> buffer) protected async ValueTask<int> ConsumePacketsAsync(Memory<byte> buffer)
{ {
if (buffer.Length < 12) if (buffer.Length < 12)
@ -55,21 +75,7 @@ internal abstract class NetSession(ILogger<NetSession> logger, NetSessionManager
if (packet == null) if (packet == null)
return consumed; return consumed;
IResult? result = await _commandDispatcher.InvokeHandler(packet); if (packet != null) await HandlePacket(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);
}
} while (buffer.Length - consumed >= 12); } while (buffer.Length - consumed >= 12);
return consumed; return consumed;

View file

@ -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.Gameserver.Game.Entity.Listener;
using NahidaImpact.Protocol; using NahidaImpact.Protocol;
@ -7,6 +8,15 @@ internal class SessionEntityEventListener(NetSession session) : IEntityEventList
{ {
private readonly NetSession _session = session; 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) public async ValueTask OnEntitySpawned(SceneEntity entity, VisionType visionType)
{ {
await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, new SceneEntityAppearNotify await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, new SceneEntityAppearNotify

View file

@ -39,6 +39,7 @@ builder.Services.AddScoped<Player>();
builder.Services.AddScoped<SceneManager>(); builder.Services.AddScoped<SceneManager>();
builder.Services.AddScoped<EntityManager>(); builder.Services.AddScoped<EntityManager>();
builder.Services.AddScoped<EntityFactory>(); builder.Services.AddScoped<EntityFactory>();
builder.Services.AddScoped<ClientActionManager>();
// Logic Listeners // Logic Listeners
builder.Services.AddScoped<IEntityEventListener, SessionEntityEventListener>(); builder.Services.AddScoped<IEntityEventListener, SessionEntityEventListener>();