Handle movement, no cooldowns for skills (reset immediately)
This commit is contained in:
parent
a762ec8241
commit
42cca1e786
13 changed files with 179 additions and 21 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
20
NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs
Normal file
20
NahidaImpact.Gameserver/Game/Scene/ClientActionManager.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs
Normal file
25
NahidaImpact.Gameserver/Helpers/UnionCmdHelper.cs
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
Loading…
Reference in a new issue