From ed3cc81862dc539872cc6bafee351b9c688ff21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Sun, 15 Jan 2023 20:19:20 +0100 Subject: [PATCH] feat(game): respond to rest, raidfhp, tp, throw, mapclear, die, char_sc packets Resolves #58 --- Core/NosSmooth.Game/Data/Maps/MapEntities.cs | 8 ++ .../Extensions/ServiceCollectionExtensions.cs | 21 ++++- .../Entities/CharScResponder.cs | 43 ++++++++++ .../PacketHandlers/Entities/DieResponder.cs | 59 +++++++++++++ .../PacketHandlers/Entities/RestResponder.cs | 43 ++++++++++ .../{Map => Entities}/ReviveResponder.cs | 2 +- .../PacketHandlers/Map/MapclearResponder.cs | 35 ++++++++ .../PacketHandlers/Map/ThrowResponder.cs | 71 ++++++++++++++++ .../PacketHandlers/Map/TpResponder.cs | 44 ++++++++++ .../PacketHandlers/Raids/RaidHpMpResponder.cs | 82 +++++++++++++++++++ .../PacketHandlers/Raids/RdlstResponder.cs | 65 ++++++++++++++- 11 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 Core/NosSmooth.Game/PacketHandlers/Entities/CharScResponder.cs create mode 100644 Core/NosSmooth.Game/PacketHandlers/Entities/DieResponder.cs create mode 100644 Core/NosSmooth.Game/PacketHandlers/Entities/RestResponder.cs rename Core/NosSmooth.Game/PacketHandlers/{Map => Entities}/ReviveResponder.cs (96%) create mode 100644 Core/NosSmooth.Game/PacketHandlers/Map/MapclearResponder.cs create mode 100644 Core/NosSmooth.Game/PacketHandlers/Map/ThrowResponder.cs create mode 100644 Core/NosSmooth.Game/PacketHandlers/Map/TpResponder.cs create mode 100644 Core/NosSmooth.Game/PacketHandlers/Raids/RaidHpMpResponder.cs diff --git a/Core/NosSmooth.Game/Data/Maps/MapEntities.cs b/Core/NosSmooth.Game/Data/Maps/MapEntities.cs index cbfa308..fb3d859 100644 --- a/Core/NosSmooth.Game/Data/Maps/MapEntities.cs +++ b/Core/NosSmooth.Game/Data/Maps/MapEntities.cs @@ -108,6 +108,14 @@ public class MapEntities RemoveEntity(entity.Id); } + /// + /// Remove all entities. + /// + internal void Clear() + { + _entities.Clear(); + } + /// /// Remove the given entity. /// diff --git a/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs b/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs index f244882..f37a101 100644 --- a/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs +++ b/Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs @@ -13,6 +13,7 @@ using NosSmooth.Game.Apis.Unsafe; using NosSmooth.Game.Contracts; using NosSmooth.Game.Events.Core; using NosSmooth.Game.Events.Inventory; +using NosSmooth.Game.Events.Ui; using NosSmooth.Game.PacketHandlers.Act4; using NosSmooth.Game.PacketHandlers.Characters; using NosSmooth.Game.PacketHandlers.Entities; @@ -22,6 +23,7 @@ using NosSmooth.Game.PacketHandlers.Raids; using NosSmooth.Game.PacketHandlers.Relations; using NosSmooth.Game.PacketHandlers.Skills; using NosSmooth.Game.PacketHandlers.Specialists; +using NosSmooth.Game.PacketHandlers.Ui; using NosSmooth.Packets.Server.Raids; namespace NosSmooth.Game.Extensions; @@ -57,6 +59,7 @@ public static class ServiceCollectionExtensions .AddPacketResponder() .AddPacketResponder() .AddPacketResponder() + .AddPacketResponder() // friends .AddPacketResponder() @@ -70,9 +73,6 @@ public static class ServiceCollectionExtensions // mates .AddPacketResponder() - // skills - .AddPacketResponder() - // map .AddPacketResponder() .AddPacketResponder() @@ -81,6 +81,14 @@ public static class ServiceCollectionExtensions .AddPacketResponder() .AddPacketResponder() .AddPacketResponder() + .AddPacketResponder() + .AddPacketResponder() + .AddPacketResponder() + + // entities + .AddPacketResponder() + .AddPacketResponder() + .AddPacketResponder() // hp, mp .AddPacketResponder() @@ -92,11 +100,16 @@ public static class ServiceCollectionExtensions .AddPacketResponder() // raids + .AddPacketResponder() .AddPacketResponder() .AddPacketResponder() .AddPacketResponder() + .AddPacketResponder() .AddPacketResponder() - .AddPacketResponder(); + .AddPacketResponder() + + // ui + .AddPacketResponder(); serviceCollection .AddTransient() diff --git a/Core/NosSmooth.Game/PacketHandlers/Entities/CharScResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Entities/CharScResponder.cs new file mode 100644 index 0000000..ac5cec0 --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Entities/CharScResponder.cs @@ -0,0 +1,43 @@ +// +// CharScResponder.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.Game.Data.Entities; +using NosSmooth.Packets.Server.Entities; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Entities; + +/// +/// A responder to . +/// +public class CharScResponder : IPacketResponder +{ + private readonly Game _game; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public CharScResponder(Game game) + { + _game = game; + } + + /// + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + var entity = _game.CurrentMap?.Entities.GetEntity(packet.EntityId); + + if (entity is not null) + { + entity.Size = packet.Size; + } + + return Task.FromResult(Result.FromSuccess()); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Entities/DieResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Entities/DieResponder.cs new file mode 100644 index 0000000..3d53305 --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Entities/DieResponder.cs @@ -0,0 +1,59 @@ +// +// DieResponder.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.Game.Data.Entities; +using NosSmooth.Game.Data.Info; +using NosSmooth.Game.Events.Core; +using NosSmooth.Game.Events.Entities; +using NosSmooth.Packets.Enums.Entities; +using NosSmooth.Packets.Server.Entities; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Entities; + +/// +/// A responder to . +/// +public class DieResponder : IPacketResponder +{ + private readonly Game _game; + private readonly EventDispatcher _eventDispatcher; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + /// The event dispatcher. + public DieResponder(Game game, EventDispatcher eventDispatcher) + { + _game = game; + _eventDispatcher = eventDispatcher; + } + + /// + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + var entity = _game.CurrentMap?.Entities.GetEntity(packet.TargetEntityId); + + if (entity is null) + { + return Task.FromResult(Result.FromSuccess()); + } + + if (entity.Type is not(EntityType.Player or EntityType.Npc)) + { + _game.CurrentMap?.Entities.RemoveEntity(entity); + } + + entity.Hp ??= new Health(); + entity.Hp.Amount = 0; + entity.Hp.Percentage = 0; + + return _eventDispatcher.DispatchEvent(new EntityDiedEvent(entity, null), ct); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Entities/RestResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Entities/RestResponder.cs new file mode 100644 index 0000000..25d2e2c --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Entities/RestResponder.cs @@ -0,0 +1,43 @@ +// +// RestResponder.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.Game.Data.Entities; +using NosSmooth.Packets.Server.UI; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Entities; + +/// +/// Responds to . +/// +public class RestResponder : IPacketResponder +{ + private readonly Game _game; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public RestResponder(Game game) + { + _game = game; + } + + /// + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + var entity = _game.CurrentMap?.Entities.GetEntity(packet.EntityId); + + if (entity is not null) + { + entity.IsSitting = packet.IsSitting; + } + + return Task.FromResult(Result.FromSuccess()); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Map/ReviveResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Entities/ReviveResponder.cs similarity index 96% rename from Core/NosSmooth.Game/PacketHandlers/Map/ReviveResponder.cs rename to Core/NosSmooth.Game/PacketHandlers/Entities/ReviveResponder.cs index 29b56ca..cb691d5 100644 --- a/Core/NosSmooth.Game/PacketHandlers/Map/ReviveResponder.cs +++ b/Core/NosSmooth.Game/PacketHandlers/Entities/ReviveResponder.cs @@ -9,7 +9,7 @@ using NosSmooth.Game.Data.Entities; using NosSmooth.Packets.Server.Entities; using Remora.Results; -namespace NosSmooth.Game.PacketHandlers.Map; +namespace NosSmooth.Game.PacketHandlers.Entities; /// /// A handler of . diff --git a/Core/NosSmooth.Game/PacketHandlers/Map/MapclearResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Map/MapclearResponder.cs new file mode 100644 index 0000000..0cfdff6 --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Map/MapclearResponder.cs @@ -0,0 +1,35 @@ +// +// MapclearResponder.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.Server.Maps; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Map; + +/// +/// A responder to . +/// +public class MapclearResponder : IPacketResponder +{ + private readonly Game _game; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public MapclearResponder(Game game) + { + _game = game; + } + + /// + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + _game.CurrentMap?.Entities.Clear(); + return Task.FromResult(Result.FromSuccess()); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Map/ThrowResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Map/ThrowResponder.cs new file mode 100644 index 0000000..9bbd83f --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Map/ThrowResponder.cs @@ -0,0 +1,71 @@ +// +// ThrowResponder.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 Microsoft.Extensions.Logging; +using NosSmooth.Core.Extensions; +using NosSmooth.Core.Packets; +using NosSmooth.Data.Abstractions; +using NosSmooth.Game.Data.Entities; +using NosSmooth.Game.Data.Info; +using NosSmooth.Packets.Server.Maps; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Map; + +/// +/// A responder to . +/// +public class ThrowResponder : IPacketResponder +{ + private readonly Game _game; + private readonly IInfoService _infoService; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + /// The info service. + /// The logger. + public ThrowResponder(Game game, IInfoService infoService, ILogger logger) + { + _game = game; + _infoService = infoService; + _logger = logger; + } + + /// + public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + var map = _game.CurrentMap; + + if (map is null) + { + return Result.FromSuccess(); + } + + var itemInfoResult = await _infoService.GetItemInfoAsync(packet.ItemVNum, ct); + if (!itemInfoResult.IsDefined(out var itemInfo)) + { + _logger.LogWarning("Could not obtain item info for vnum {vnum}: {error}", packet.ItemVNum, itemInfoResult.ToFullString()); + } + + map.Entities.AddEntity + ( + new GroundItem + { + Position = new Position(packet.TargetX, packet.TargetY), + Amount = packet.Amount, + Id = packet.DropId, + ItemInfo = itemInfo, + VNum = packet.ItemVNum + } + ); + + return Result.FromSuccess(); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Map/TpResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Map/TpResponder.cs new file mode 100644 index 0000000..537ff7d --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Map/TpResponder.cs @@ -0,0 +1,44 @@ +// +// TpResponder.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.Game.Data.Entities; +using NosSmooth.Game.Data.Info; +using NosSmooth.Packets.Server.Maps; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Map; + +/// +/// A responder to . +/// +public class TpResponder : IPacketResponder +{ + private readonly Game _game; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public TpResponder(Game game) + { + _game = game; + } + + /// + public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + var entity = _game.CurrentMap?.Entities.GetEntity(packet.EntityId); + + if (entity is not null) + { + entity.Position = new Position(packet.PositionX, packet.PositionY); + } + + return Task.FromResult(Result.FromSuccess()); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Raids/RaidHpMpResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Raids/RaidHpMpResponder.cs new file mode 100644 index 0000000..a202da9 --- /dev/null +++ b/Core/NosSmooth.Game/PacketHandlers/Raids/RaidHpMpResponder.cs @@ -0,0 +1,82 @@ +// +// RaidHpMpResponder.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.Game.Data.Info; +using NosSmooth.Game.Data.Social; +using NosSmooth.Packets.Server.Raids; +using Remora.Results; + +namespace NosSmooth.Game.PacketHandlers.Raids; + +/// +/// A responder to , . +/// +public class RaidHpMpResponder : IPacketResponder, IPacketResponder +{ + private readonly Game _game; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public RaidHpMpResponder(Game game) + { + _game = game; + } + + /// + public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + + await _game.UpdateRaidAsync + ( + raid => + { + foreach (var member in raid.Members ?? (IReadOnlyList)Array.Empty()) + { + var data = packet.HpSubPackets?.FirstOrDefault(x => x.PlayerId == member.PlayerId); + + if (data is not null) + { + member.Hp ??= new Health(); + member.Hp.Percentage = data.HpPercentage; + } + } + return raid; + }, + ct: ct + ); + return Result.FromSuccess(); + } + + /// + public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + + await _game.UpdateRaidAsync + ( + raid => + { + foreach (var member in raid.Members ?? (IReadOnlyList)Array.Empty()) + { + var data = packet.HpSubPackets?.FirstOrDefault(x => x.PlayerId == member.PlayerId); + + if (data is not null) + { + member.Mp ??= new Health(); + member.Mp.Percentage = data.HpPercentage; + } + } + return raid; + }, + ct: ct + ); + return Result.FromSuccess(); + } +} \ No newline at end of file diff --git a/Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs b/Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs index dc2e2f3..67790c2 100644 --- a/Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs +++ b/Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs @@ -17,7 +17,7 @@ namespace NosSmooth.Game.PacketHandlers.Raids; /// /// A responder to . /// -public class RdlstResponder : IPacketResponder +public class RdlstResponder : IPacketResponder, IPacketResponder { private readonly Game _game; private readonly EventDispatcher _eventDispatcher; @@ -95,4 +95,67 @@ public class RdlstResponder : IPacketResponder return Result.FromSuccess(); } + + /// + public async Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) + { + var packet = packetArgs.Packet; + + IReadOnlyList UpdateMembers(IReadOnlyList? currentMembers) + { + return packet.Players + .Select + ( + packetMember => + { + var newMember = currentMembers?.FirstOrDefault + (member => packetMember.Id == member.PlayerId) ?? new GroupMember(packetMember.Id); + + newMember.Class = packetMember.Class; + newMember.Level = packetMember.Level; + newMember.HeroLevel = packetMember.HeroLevel; + newMember.Sex = packetMember.Sex; + newMember.MorphVNum = packetMember.MorphVNum; + + return newMember; + } + ).ToArray(); + } + + Raid? prevRaid = null; + var currentRaid = await _game.CreateOrUpdateRaidAsync + ( + () => new Raid + ( + packet.RaidType, + RaidState.Waiting, + packet.MinimumLevel, + packet.MaximumLevel, + null, + null, + null, + null, + UpdateMembers(null) + ), + raid => + { + prevRaid = raid; + return raid with + { + Type = packet.RaidType, + MinimumLevel = packet.MinimumLevel, + MaximumLevel = packet.MaximumLevel, + Members = UpdateMembers(raid.Members), + }; + }, + ct: ct + ); + + if (prevRaid is null && currentRaid is not null) + { + return await _eventDispatcher.DispatchEvent(new RaidJoinedEvent(currentRaid), ct); + } + + return Result.FromSuccess(); + } } \ No newline at end of file -- 2.48.1