//
// 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 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.Game.Data.Social;
using NosSmooth.Game.Events.Core;
using NosSmooth.Game.Events.Entities;
using NosSmooth.Game.Helpers;
using NosSmooth.Packets.Enums.Entities;
using NosSmooth.Packets.Server.Maps;
using Remora.Results;
namespace NosSmooth.Game.PacketHandlers.Map;
///
/// Responds to in packet.
///
public class InResponder : IPacketResponder
{
private readonly Game _game;
private readonly EventDispatcher _eventDispatcher;
private readonly IInfoService _infoService;
private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// The game.
/// The event dispatcher.
/// The info service.
/// The logger.
public InResponder
(
Game game,
EventDispatcher eventDispatcher,
IInfoService infoService,
ILogger logger
)
{
_game = game;
_eventDispatcher = eventDispatcher;
_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 entities = map.Entities;
// add entity to the map
var entity = await CreateEntityFromInPacket(packet, ct);
entities.AddEntity(entity);
return await _eventDispatcher.DispatchEvent(new EntityJoinedMapEvent(entity), ct);
}
private async Task CreateEntityFromInPacket(InPacket packet, CancellationToken ct)
{
if (packet.ItemSubPacket is not null)
{
return await CreateGroundItem(packet, packet.ItemSubPacket, ct);
}
if (packet.PlayerSubPacket is not null)
{
return await CreatePlayer(packet, packet.PlayerSubPacket, ct);
}
if (packet.NonPlayerSubPacket is not null)
{
if (packet.EntityType == EntityType.Npc)
{
return await CreateNpc(packet, packet.NonPlayerSubPacket, ct);
}
return await CreateMonster(packet, packet.NonPlayerSubPacket, ct);
}
throw new Exception("The in packet did not contain any subpacket. Bug?");
}
private async Task CreateGroundItem
(InPacket packet, InItemSubPacket itemSubPacket, CancellationToken ct)
{
if (packet.VNum is null)
{
throw new Exception("The vnum from the in packet cannot be null for items.");
}
var itemInfoResult = await _infoService.GetItemInfoAsync(packet.VNum.Value, ct);
if (!itemInfoResult.IsDefined(out var itemInfo))
{
_logger.LogWarning
(
"Could not obtain an item info for vnum {vnum}: {error}",
packet.VNum.Value,
itemInfoResult.ToFullString()
);
}
return new GroundItem
{
Amount = itemSubPacket.Amount,
Id = packet.EntityId,
OwnerId = itemSubPacket.OwnerId,
IsQuestRelated = itemSubPacket.IsQuestRelative,
ItemInfo = itemInfo,
Position = new Position(packet.PositionX, packet.PositionY),
VNum = packet.VNum.Value,
};
}
private async Task CreatePlayer(InPacket packet, InPlayerSubPacket playerSubPacket, CancellationToken ct)
{
return new Player
{
Position = new Position(packet.PositionX, packet.PositionY),
Id = packet.EntityId,
Name = packet.Name?.Name,
ArenaWinner = playerSubPacket.ArenaWinner,
Class = playerSubPacket.Class,
Compliment = playerSubPacket.Compliment,
Direction = packet.Direction,
Equipment = await EquipmentHelpers.CreateEquipmentFromInSubpacketAsync
(
_infoService,
playerSubPacket.Equipment,
playerSubPacket.WeaponUpgradeRareSubPacket,
playerSubPacket.ArmorUpgradeRareSubPacket,
ct
),
Faction = playerSubPacket.Faction,
Size = playerSubPacket.Size,
Authority = playerSubPacket.Authority,
Sex = playerSubPacket.Sex,
HairStyle = playerSubPacket.HairStyle,
HairColor = playerSubPacket.HairColor,
Icon = playerSubPacket.ReputationIcon,
IsInvisible = playerSubPacket.IsInvisible,
Title = playerSubPacket.Title,
Level = playerSubPacket.Level,
HeroLevel = playerSubPacket.HeroLevel,
Morph = new Morph(playerSubPacket.MorphVNum, playerSubPacket.MorphUpgrade),
Family = playerSubPacket.FamilySubPacket.Value is null
? null
: new Family
(
playerSubPacket.FamilySubPacket.Value.FamilyId,
playerSubPacket.FamilySubPacket.Value.Title,
playerSubPacket.FamilyName,
playerSubPacket.Level,
playerSubPacket.FamilyIcons
),
};
}
private async Task CreateNpc
(InPacket packet, InNonPlayerSubPacket nonPlayerSubPacket, CancellationToken ct)
{
if (packet.VNum is null)
{
throw new Exception("The vnum from the in packet cannot be null for monsters.");
}
var monsterInfoResult = await _infoService.GetMonsterInfoAsync(packet.VNum.Value, ct);
if (!monsterInfoResult.IsDefined(out var monsterInfo))
{
_logger.LogWarning
(
"Could not obtain a monster info for vnum {vnum}: {error}",
packet.VNum.Value,
monsterInfoResult.ToFullString()
);
}
return new Npc
{
VNum = packet.VNum.Value,
NpcInfo = monsterInfo,
Id = packet.EntityId,
Direction = packet.Direction,
Faction = nonPlayerSubPacket.Faction,
Hp = new Health { Percentage = nonPlayerSubPacket.HpPercentage },
Mp = new Health { Percentage = nonPlayerSubPacket.MpPercentage },
Name = nonPlayerSubPacket.Name?.Name,
Position = new Position(packet.PositionX, packet.PositionY),
IsInvisible = nonPlayerSubPacket.IsInvisible,
Level = monsterInfo?.Level ?? null,
IsSitting = nonPlayerSubPacket.IsSitting
};
}
private async Task CreateMonster
(InPacket packet, InNonPlayerSubPacket nonPlayerSubPacket, CancellationToken ct)
{
if (packet.VNum is null)
{
throw new Exception("The vnum from the in packet cannot be null for monsters.");
}
var monsterInfoResult = await _infoService.GetMonsterInfoAsync(packet.VNum.Value, ct);
if (!monsterInfoResult.IsDefined(out var monsterInfo))
{
_logger.LogWarning
(
"Could not obtain a monster info for vnum {vnum}: {error}",
packet.VNum.Value,
monsterInfoResult.ToFullString()
);
}
return new Monster
{
VNum = packet.VNum.Value,
MonsterInfo = monsterInfo,
Id = packet.EntityId,
Direction = packet.Direction,
Faction = nonPlayerSubPacket.Faction,
Hp = new Health { Percentage = nonPlayerSubPacket.HpPercentage },
Mp = new Health { Percentage = nonPlayerSubPacket.MpPercentage },
Name = nonPlayerSubPacket.Name?.Name,
Position = new Position(packet.PositionX, packet.PositionY),
IsInvisible = nonPlayerSubPacket.IsInvisible,
Level = monsterInfo?.Level ?? null,
IsSitting = nonPlayerSubPacket.IsSitting
};
}
}