~ruther/NosSmooth

419415627683d961c9a0f060e4e52d422baea40e — Rutherther 2 years ago 5c85015
feat(pathfinding): save mates state
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());
    }

Do not follow this link