A Extensions/NosSmooth.Extensions.Pathfinding/EntityState.cs => Extensions/NosSmooth.Extensions.Pathfinding/EntityState.cs +28 -0
@@ 0,0 1,28 @@
+//
+// EntityState.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace NosSmooth.Extensions.Pathfinding;
+
+/// <summary>
+/// A state, position of an entity.
+/// </summary>
+internal class EntityState
+{
+ /// <summary>
+ /// Gets id of the entity.
+ /// </summary>
+ internal long Id { get; init; }
+
+ /// <summary>
+ /// Gets or sets the x coordinate of the entity.
+ /// </summary>
+ internal short X { get; set; }
+
+ /// <summary>
+ /// Gets or sets the y coordinate of the entity.
+ /// </summary>
+ internal short Y { get; set; }
+}<
\ No newline at end of file
A Extensions/NosSmooth.Extensions.Pathfinding/Errors/EntityStateNotFoundError.cs => Extensions/NosSmooth.Extensions.Pathfinding/Errors/EntityStateNotFoundError.cs +11 -0
@@ 0,0 1,11 @@
+//
+// EntityStateNotFoundError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Extensions.Pathfinding.Errors;
+
+public record EntityStateNotFoundError(long EntityId) : ResultError($"Could not find pathfinder state of entity {EntityId}");<
\ No newline at end of file
M Extensions/NosSmooth.Extensions.Pathfinding/Extensions/ServiceCollectionExtensions.cs => Extensions/NosSmooth.Extensions.Pathfinding/Extensions/ServiceCollectionExtensions.cs +2 -0
@@ 34,6 34,8 @@ public static class ServiceCollectionExtensions
.AddPacketResponder<SuPacketResponder>()
.AddPacketResponder<TpPacketResponder>()
.AddPacketResponder<CInfoPacketResponder>()
+ .AddPacketResponder<InResponder>()
+ .AddPacketResponder<PtctlResponder>()
.AddSingleton<WalkManager>()
.AddSingleton<Pathfinder>()
.AddSingleton<PathfinderState>();
M Extensions/NosSmooth.Extensions.Pathfinding/Pathfinder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Pathfinder.cs +28 -2
@@ 29,7 29,7 @@ public class Pathfinder
}
/// <summary>
- /// Attempts to find a path between the current position and the target.
+ /// Attempts to find a path between the current character position and the target.
/// </summary>
/// <param name="targetX">The target x coordinate.</param>
/// <param name="targetY">The target y coordinate.</param>
@@ 39,7 39,33 @@ public class Pathfinder
short targetX,
short targetY
)
- => FindPathFrom(_state.X, _state.Y, targetX, targetY);
+ => FindPathFrom(_state.Character.X, _state.Character.Y, targetX, targetY);
+
+ /// <summary>
+ /// Attempts to find a path between the current position of the entity and the target.
+ /// </summary>
+ /// <remarks>
+ /// Works only for entities that are stored in state,
+ /// that means only the character and mates owned by the character.
+ /// </remarks>
+ /// <param name="entityId">The id of the entity to find path from.</param>
+ /// <param name="targetX">The target x coordinate.</param>
+ /// <param name="targetY">The target y coordinate.</param>
+ /// <returns>A path or an error.</returns>
+ public Result<Path> FindPathFromEntity
+ (
+ long entityId,
+ short targetX,
+ short targetY
+ )
+ {
+ if (!_state.Entities.TryGetValue(entityId, out var entityState))
+ {
+ return new EntityStateNotFoundError(entityId);
+ }
+
+ return FindPathFrom(entityState.X, entityState.Y, targetX, targetY);
+ }
/// <summary>
/// Attempts to find a path between the given positions on the current map.
M Extensions/NosSmooth.Extensions.Pathfinding/PathfinderState.cs => Extensions/NosSmooth.Extensions.Pathfinding/PathfinderState.cs +75 -6
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Collections.Concurrent;
using NosSmooth.Core.Client;
using NosSmooth.Core.Stateful;
using NosSmooth.Data.Abstractions.Infos;
@@ 15,6 16,27 @@ namespace NosSmooth.Extensions.Pathfinding;
/// </summary>
public class PathfinderState : IStatefulEntity
{
+ private ConcurrentDictionary<long, EntityState> _entities;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PathfinderState"/> class.
+ /// </summary>
+ public PathfinderState()
+ {
+ _entities = new ConcurrentDictionary<long, EntityState>();
+ Character = new EntityState();
+ }
+
+ /// <summary>
+ /// Gets the entities.
+ /// </summary>
+ internal IReadOnlyDictionary<long, EntityState> Entities => _entities;
+
+ /// <summary>
+ /// Gets the character entity state.
+ /// </summary>
+ internal EntityState Character { get; private set; }
+
/// <summary>
/// Gets or sets the current map id.
/// </summary>
@@ 26,17 48,64 @@ public class PathfinderState : IStatefulEntity
internal IMapInfo? MapInfo { get; set; }
/// <summary>
- /// Gets or sets the current x.
+ /// Sets the id of the character entity.
/// </summary>
- internal short X { get; set; }
+ /// <param name="characterId">The character id.</param>
+ internal void SetCharacterId(long characterId)
+ {
+ EntityState GetCharacter()
+ {
+ Character = new EntityState
+ {
+ Id = characterId,
+ X = Character.X,
+ Y = Character.Y
+ };
+
+ return Character;
+ }
+
+ _entities.TryRemove(Character.Id, out _);
+ _entities.AddOrUpdate
+ (
+ characterId,
+ _ => GetCharacter(),
+ (_, _) => GetCharacter()
+ );
+ }
/// <summary>
- /// Gets or sets the current y.
+ /// Add the given entity to the list.
/// </summary>
- internal short Y { get; set; }
+ /// <param name="entityId">The id of the entity.</param>
+ /// <param name="x">The x coordinate of the entity.</param>
+ /// <param name="y">The y coordinate of the entity.</param>
+ internal void AddEntity
+ (
+ long entityId,
+ short x,
+ short y
+ )
+ {
+ EntityState GetEntity()
+ {
+ return new EntityState
+ {
+ Id = entityId,
+ X = x,
+ Y = y
+ };
+ }
+
+ _entities.AddOrUpdate(entityId, _ => GetEntity(), (_, _) => GetEntity());
+ }
/// <summary>
- /// Gets or sets the id of the charcter.
+ /// Remove all entities from the list.
/// </summary>
- internal long CharacterId { get; set; }
+ internal void ClearEntities()
+ {
+ _entities.Clear();
+ SetCharacterId(Character.Id);
+ }
}=
\ No newline at end of file
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/AtResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/AtResponder.cs +2 -2
@@ 30,8 30,8 @@ internal class AtResponder : IPacketResponder<AtPacket>
{
var packet = packetArgs.Packet;
- _state.X = packet.X;
- _state.Y = packet.Y;
+ _state.Character.X = packet.X;
+ _state.Character.Y = packet.Y;
return Task.FromResult(Result.FromSuccess());
}
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/CInfoPacketResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/CInfoPacketResponder.cs +1 -1
@@ 28,7 28,7 @@ public class CInfoPacketResponder : IPacketResponder<CInfoPacket>
/// <inheritdoc />
public Task<Result> Respond(PacketEventArgs<CInfoPacket> packetArgs, CancellationToken ct = default)
{
- _state.CharacterId = packetArgs.Packet.CharacterId;
+ _state.SetCharacterId(packetArgs.Packet.CharacterId);
return Task.FromResult(Result.FromSuccess());
}
}=
\ No newline at end of file
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/CMapResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/CMapResponder.cs +1 -0
@@ 31,6 31,7 @@ internal class CMapResponder : IPacketResponder<CMapPacket>
/// <inheritdoc />
public async Task<Result> Respond(PacketEventArgs<CMapPacket> packetArgs, CancellationToken ct = default)
{
+ _state.ClearEntities();
var packet = packetArgs.Packet;
_state.MapId = packet.Id;
A Extensions/NosSmooth.Extensions.Pathfinding/Responders/InResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/InResponder.cs +53 -0
@@ 0,0 1,53 @@
+//
+// InResponder.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using NosSmooth.Core.Packets;
+using NosSmooth.Packets.Client.Battle;
+using NosSmooth.Packets.Enums.Entities;
+using NosSmooth.Packets.Server.Maps;
+using Remora.Results;
+
+namespace NosSmooth.Extensions.Pathfinding.Responders;
+
+/// <inheritdoc />
+public class InResponder : IPacketResponder<InPacket>
+{
+ private readonly PathfinderState _state;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InResponder"/> class.
+ /// </summary>
+ /// <param name="state">The pathfinder state.</param>
+ public InResponder(PathfinderState state)
+ {
+ _state = state;
+
+ }
+
+ /// <inheritdoc />
+ public Task<Result> Respond(PacketEventArgs<InPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+
+ if (packet.EntityType != EntityType.Npc)
+ {
+ return Task.FromResult(Result.FromSuccess());
+ }
+
+ if (packet.NonPlayerSubPacket is null)
+ {
+ return Task.FromResult(Result.FromSuccess());
+ }
+
+ if (packet.NonPlayerSubPacket.OwnerId != _state.Character.Id)
+ {
+ return Task.FromResult(Result.FromSuccess());
+ }
+
+ _state.AddEntity(packet.EntityId, packet.PositionX, packet.PositionY);
+ return Task.FromResult(Result.FromSuccess());
+ }
+}<
\ No newline at end of file
A Extensions/NosSmooth.Extensions.Pathfinding/Responders/PtctlResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/PtctlResponder.cs +47 -0
@@ 0,0 1,47 @@
+//
+// PtctlResponder.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Security;
+using NosSmooth.Core.Packets;
+using NosSmooth.Packets.Client.Mates;
+using Remora.Results;
+
+namespace NosSmooth.Extensions.Pathfinding.Responders;
+
+/// <inheritdoc />
+public class PtctlResponder : IPacketResponder<PtctlPacket>
+{
+ private readonly PathfinderState _state;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PtctlResponder"/> class.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ public PtctlResponder(PathfinderState state)
+ {
+ _state = state;
+ }
+
+ /// <inheritdoc />
+ public Task<Result> Respond(PacketEventArgs<PtctlPacket> packetArgs, CancellationToken ct = default)
+ {
+ var packet = packetArgs.Packet;
+
+ foreach (var walkControl in packet.Controls)
+ {
+ if (!_state.Entities.TryGetValue(walkControl.MateTransportId, out var entityState))
+ {
+ _state.AddEntity(walkControl.MateTransportId, walkControl.PositionX, walkControl.PositionY);
+ continue;
+ }
+
+ entityState.X = walkControl.PositionX;
+ entityState.Y = walkControl.PositionY;
+ }
+
+ return Task.FromResult(Result.FromSuccess());
+ }
+}<
\ No newline at end of file
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/SuPacketResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/SuPacketResponder.cs +4 -3
@@ 29,7 29,8 @@ public class SuPacketResponder : IPacketResponder<SuPacket>
public Task<Result> Respond(PacketEventArgs<SuPacket> packetArgs, CancellationToken ct = default)
{
var packet = packetArgs.Packet;
- if (packet.CasterEntityId == _state.CharacterId)
+
+ if (_state.Entities.TryGetValue(packet.CasterEntityId, out var entityState))
{
if (packet.PositionX is null || packet.PositionY is null)
{
@@ 41,8 42,8 @@ public class SuPacketResponder : IPacketResponder<SuPacket>
return Task.FromResult(Result.FromSuccess());
}
- _state.X = packet.PositionX.Value;
- _state.Y = packet.PositionY.Value;
+ entityState.X = packet.PositionX.Value;
+ entityState.Y = packet.PositionY.Value;
}
return Task.FromResult(Result.FromSuccess());
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/TpPacketResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/TpPacketResponder.cs +3 -3
@@ 28,10 28,10 @@ public class TpPacketResponder : IPacketResponder<TpPacket>
public Task<Result> Respond(PacketEventArgs<TpPacket> packetArgs, CancellationToken ct = default)
{
var packet = packetArgs.Packet;
- if (packet.EntityId == _state.CharacterId)
+ if (_state.Entities.TryGetValue(packet.EntityId, out var entityState))
{
- _state.X = packet.PositionX;
- _state.Y = packet.PositionY;
+ entityState.X = packet.PositionX;
+ entityState.Y = packet.PositionY;
}
return Task.FromResult(Result.FromSuccess());
M Extensions/NosSmooth.Extensions.Pathfinding/Responders/WalkResponder.cs => Extensions/NosSmooth.Extensions.Pathfinding/Responders/WalkResponder.cs +2 -2
@@ 29,8 29,8 @@ internal class WalkResponder : IPacketResponder<WalkPacket>
{
var packet = packetArgs.Packet;
- _state.X = packet.PositionX;
- _state.Y = packet.PositionY;
+ _state.Character.X = packet.PositionX;
+ _state.Character.Y = packet.PositionY;
return Task.FromResult(Result.FromSuccess());
}