feat: add exceptions

This commit is contained in:
k0t9i 2023-10-18 13:28:29 +04:00
parent a2428bb0d1
commit 521af7c00e
15 changed files with 313 additions and 151 deletions

View File

@ -0,0 +1,62 @@
#pragma once
#include <stdexcept>
#include <string>
#include <stdlib.h>
namespace L2Bot::Domain
{
class RuntimeException : public std::runtime_error
{
public:
using _Mybase = std::runtime_error;
explicit RuntimeException(const std::wstring& _Message) : _Mybase(convert(_Message)), m_Message(_Message) {}
explicit RuntimeException(const wchar_t* _Message) : RuntimeException(std::wstring(_Message)) {}
/// returns the explanatory string
const char* what() const noexcept override
{
return std::runtime_error::what();
}
const std::wstring& Message() const noexcept
{
return m_Message;
}
#if !_HAS_EXCEPTIONS
protected:
virtual void _Doraise() const override { // perform class-specific exception handling
_RAISE(*this);
}
#endif // !_HAS_EXCEPTIONS
private:
const std::string convert(const std::wstring& wmsg) const
{
const auto size = wmsg.size() + 1;
char* msg = new char[size];
size_t charsConverted = 0;
wcstombs_s(&charsConverted, msg, size, wmsg.c_str(), wmsg.length());
const std::string result(msg);
delete[] msg;
return result;
}
private:
const std::wstring m_Message;
};
class CriticalRuntimeException : public RuntimeException
{
public:
explicit CriticalRuntimeException(const std::wstring& _Message) : RuntimeException(_Message) {}
explicit CriticalRuntimeException(const wchar_t* _Message) : RuntimeException(_Message) {}
};
}

View File

@ -33,7 +33,7 @@ namespace L2Bot::Domain::Logger
struct tm timeinfo; struct tm timeinfo;
localtime_s(&timeinfo, &rawTime); localtime_s(&timeinfo, &rawTime);
std::wstringstream oss; std::wstringstream oss;
oss << "[" << std::put_time(&timeinfo, L"%Y-%m-%d %H.%M.%S") << "]"; oss << "[" << std::put_time(&timeinfo, L"%Y-%m-%d %H:%M:%S") << "]";
return oss.str(); return oss.str();
} }

View File

@ -35,9 +35,30 @@ namespace L2Bot::Domain::Logger
Log(LogLevel::app, format, args...); Log(LogLevel::app, format, args...);
} }
void Error(const std::wstring& message) const
{
Log(LogLevel::error, message);
}
void Warning(const std::wstring& message) const
{
Log(LogLevel::warning, message);
}
void Info(const std::wstring& message) const
{
Log(LogLevel::info, message);
}
void App(const std::wstring& message) const
{
Log(LogLevel::app, message);
}
private: private:
template <class ... Args> template <class ... Args>
void Log(LogLevel level, const std::wformat_string<Args...> format, Args... args) const void Log(LogLevel level, const std::wformat_string<Args...> format, Args... args) const
{
Log(level, std::vformat(format.get(), std::make_wformat_args(args...)));
}
void Log(LogLevel level, const std::wstring& message) const
{ {
std::wstring prefix = L""; std::wstring prefix = L"";
if (level == LogLevel::error) { if (level == LogLevel::error) {
@ -55,7 +76,7 @@ namespace L2Bot::Domain::Logger
for (const auto& channel : m_Channels) { for (const auto& channel : m_Channels) {
if (channel->IsAppropriateLevel(level)) { if (channel->IsAppropriateLevel(level)) {
channel->SendToChannel(prefix + std::vformat(format.get(), std::make_wformat_args(args...))); channel->SendToChannel(prefix + message);
} }
} }
} }

View File

@ -10,6 +10,8 @@
#include "../DTO/Message.h" #include "../DTO/Message.h"
#include "../Services/IncomingMessageProcessor.h" #include "../Services/IncomingMessageProcessor.h"
#include "../Services/OutgoingMessageBuilder.h" #include "../Services/OutgoingMessageBuilder.h"
#include "../Exceptions.h"
#include "../Services/ServiceLocator.h"
namespace L2Bot::Domain::Services namespace L2Bot::Domain::Services
{ {
@ -69,19 +71,30 @@ namespace L2Bot::Domain::Services
{ {
while (!m_Stopped) while (!m_Stopped)
{ {
const auto& messages = GetOutgoingMessages(); try {
const auto& messages = GetOutgoingMessages();
if (m_Transport.IsConnected()) if (m_Transport.IsConnected())
{
for (const auto& message : messages)
{ {
m_Transport.Send( for (const auto& message : messages)
m_Serializer.Serialize(message) {
); m_Transport.Send(
m_Serializer.Serialize(message)
);
}
} }
}
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
catch (const CriticalRuntimeException& e)
{
m_Stopped = true;
ServiceLocator::GetInstance().GetLogger()->Error(e.Message());
}
catch (const RuntimeException& e)
{
ServiceLocator::GetInstance().GetLogger()->Warning(e.Message());
}
} }
} }
@ -89,15 +102,28 @@ namespace L2Bot::Domain::Services
{ {
while (!m_Stopped) while (!m_Stopped)
{ {
if (m_Transport.IsConnected()) try {
{ if (m_Transport.IsConnected())
const auto messageType = m_IncomingMessageProcessor.Process(m_Transport.Receive()); {
const auto& message = m_Transport.Receive();
ServiceLocator::GetInstance().GetLogger()->Info(L"received message from client: {}", message);
const auto messageType = m_IncomingMessageProcessor.Process(message);
if (messageType == Serializers::IncomingMessage::Type::invalidate) { if (messageType == Serializers::IncomingMessage::Type::invalidate) {
Invalidate(); Invalidate();
}
} }
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
catch (const CriticalRuntimeException& e)
{
m_Stopped = true;
ServiceLocator::GetInstance().GetLogger()->Error(e.Message());
}
catch (const RuntimeException& e)
{
ServiceLocator::GetInstance().GetLogger()->Warning(e.Message());
} }
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
} }
@ -105,11 +131,22 @@ namespace L2Bot::Domain::Services
{ {
while (!m_Stopped) while (!m_Stopped)
{ {
if (!m_Transport.IsConnected()) try {
{ if (!m_Transport.IsConnected())
m_Transport.Connect(); {
m_Transport.Connect();
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
catch (const CriticalRuntimeException& e)
{
m_Stopped = true;
ServiceLocator::GetInstance().GetLogger()->Error(e.Message());
}
catch (const RuntimeException& e)
{
ServiceLocator::GetInstance().GetLogger()->Warning(e.Message());
} }
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} }
} }

View File

@ -229,6 +229,7 @@
<ClInclude Include="Domain\ValueObjects\Transform.h" /> <ClInclude Include="Domain\ValueObjects\Transform.h" />
<ClInclude Include="Domain\ValueObjects\VariableStats.h" /> <ClInclude Include="Domain\ValueObjects\VariableStats.h" />
<ClInclude Include="Domain\ValueObjects\VitalStats.h" /> <ClInclude Include="Domain\ValueObjects\VitalStats.h" />
<ClInclude Include="Domain\Exceptions.h" />
<ClInclude Include="framework.h" /> <ClInclude Include="framework.h" />
<ClInclude Include="Domain\Helpers\HashCombiner.h" /> <ClInclude Include="Domain\Helpers\HashCombiner.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />

View File

@ -234,6 +234,9 @@
<ClInclude Include="Domain\Logger\Logger.h"> <ClInclude Include="Domain\Logger\Logger.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Domain\Exceptions.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">

View File

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
#include <format>
#include "Domain/Exceptions.h"
#include "Domain/Services/ServiceLocator.h" #include "Domain/Services/ServiceLocator.h"
#define BUFFER_SIZE 16384 #define BUFFER_SIZE 16384
@ -38,8 +40,7 @@ public:
if (m_Pipe == INVALID_HANDLE_VALUE) if (m_Pipe == INVALID_HANDLE_VALUE)
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot connect to the pipe ""{}""", m_PipeName); throw RuntimeException(std::format(L"cannot create the pipe {}: {}", m_PipeName, GetLastError()));
return false;
} }
} }
else else
@ -81,14 +82,14 @@ public:
const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_WritingOverlapped, &ret, false); const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_WritingOverlapped, &ret, false);
if (!overlappedResult) if (!overlappedResult)
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot get overlapped result for the pipe ""{}"" when writing", m_PipeName);
m_Connected = false; m_Connected = false;
throw RuntimeException(std::format(L"cannot get overlapped result for the pipe {} when writing", m_PipeName));
} }
} }
else else
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot write to the pipe ""{}"": {}", m_PipeName, lastError);
m_Connected = false; m_Connected = false;
throw RuntimeException(std::format(L"cannot write to the pipe {}: {}", m_PipeName, lastError));
} }
} }
} }
@ -114,16 +115,14 @@ public:
const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_ReadingOverlapped, &ret, false); const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_ReadingOverlapped, &ret, false);
if (!overlappedResult) if (!overlappedResult)
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot get overlapped result for the pipe ""{}"" when reading", m_PipeName);
m_Connected = false; m_Connected = false;
return L""; throw RuntimeException(std::format(L"cannot get overlapped result for the pipe {} when reading", m_PipeName));
} }
} }
else else
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot read from the pipe ""{}"": {}", m_PipeName, lastError);
m_Connected = false; m_Connected = false;
return L""; throw RuntimeException(std::format(L"cannot read from the pipe {}: {}", m_PipeName, lastError));
} }
} }
@ -152,7 +151,7 @@ private:
const bool connected = ConnectNamedPipe(m_Pipe, &m_ConntectingOverlapped) == 0; const bool connected = ConnectNamedPipe(m_Pipe, &m_ConntectingOverlapped) == 0;
if (!connected) if (!connected)
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot connect to the pipe ""{}"": {}", m_PipeName, GetLastError()); throw RuntimeException(std::format(L"cannot connect the pipe {}: {}", m_PipeName, GetLastError()));
} }
switch (GetLastError()) switch (GetLastError())
@ -165,7 +164,7 @@ private:
if (SetEvent(m_ConntectingOverlapped.hEvent)) if (SetEvent(m_ConntectingOverlapped.hEvent))
break; break;
default: default:
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"an error has occurred when connecting to the pipe ""{}"": {}", m_PipeName, GetLastError()); throw RuntimeException(std::format(L"an error has occurred when connecting to the pipe ""{}"": {}", m_PipeName, GetLastError()));
} }
} }
@ -176,8 +175,7 @@ private:
overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (overlapped.hEvent == NULL) if (overlapped.hEvent == NULL)
{ {
Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot create overlapped for the pipe ""{}"": {}", m_PipeName, GetLastError()); throw RuntimeException(std::format(L"cannot create overlapped for the pipe {}: {}", m_PipeName, GetLastError()));
return;
} }
overlapped.Offset = 0; overlapped.Offset = 0;
overlapped.OffsetHigh = 0; overlapped.OffsetHigh = 0;

View File

@ -7,6 +7,7 @@
#include "../GameStructs/FName.h" #include "../GameStructs/FName.h"
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/AbnormalEffect.h" #include "Domain/Entities/AbnormalEffect.h"
#include "Domain/Exceptions.h"
using namespace L2Bot::Domain; using namespace L2Bot::Domain;
@ -28,15 +29,17 @@ namespace Interlude
{ {
const auto data = m_L2GameData.GetMSData(skillId, level); const auto data = m_L2GameData.GetMSData(skillId, level);
const auto name = data && data->name ? data->name : L""; if (!data) {
const auto description = data && data->description ? data->description : L""; throw RuntimeException(std::format(L"cannot load MSData for abnormal effect {}", skillId));
const auto iconEntry = data ? m_FName.GetEntry(data->iconNameIndex) : nullptr; }
const auto iconEntry = m_FName.GetEntry(data->iconNameIndex);
return std::make_shared<Entities::AbnormalEffect>( return std::make_shared<Entities::AbnormalEffect>(
skillId, skillId,
static_cast<uint8_t>(level), static_cast<uint8_t>(level),
std::wstring(name), data->name ? std::wstring(data->name) : L"",
std::wstring(description), data->description ? std::wstring(data->description) : L"",
iconEntry ? std::wstring(iconEntry->value) : L"" iconEntry ? std::wstring(iconEntry->value) : L""
); );
} }

View File

@ -1,11 +1,13 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <format>
#include "../GameStructs/L2GameDataWrapper.h" #include "../GameStructs/L2GameDataWrapper.h"
#include "../GameStructs/FName.h" #include "../GameStructs/FName.h"
#include "../GameStructs/GameStructs.h" #include "../GameStructs/GameStructs.h"
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/Drop.h" #include "Domain/Entities/Drop.h"
#include "Domain/Exceptions.h"
namespace Interlude namespace Interlude
{ {
@ -63,8 +65,15 @@ namespace Interlude
const Data GetData(const Item* item) const const Data GetData(const Item* item) const
{ {
const auto itemData = m_L2GameData.GetItemData(item->itemId); const auto itemData = m_L2GameData.GetItemData(item->itemId);
const auto nameEntry = itemData ? m_FName.GetEntry(itemData->nameIndex) : nullptr; if (!itemData) {
const auto iconEntry = itemData ? m_FName.GetEntry(itemData->iconNameIndex) : nullptr; throw RuntimeException(std::format(L"cannot load ItemData for drop {}", item->itemId));
}
const auto nameEntry = m_FName.GetEntry(itemData->nameIndex);
const auto iconEntry = m_FName.GetEntry(itemData->iconNameIndex);
if (!item->pawn) {
throw RuntimeException(std::format(L"pawn is empty for drop {}", std::wstring(nameEntry->value)));
}
return { return {
item->objectId, item->objectId,

View File

@ -1,9 +1,11 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <format>
#include "../GameStructs/GameStructs.h" #include "../GameStructs/GameStructs.h"
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/Hero.h" #include "Domain/Entities/Hero.h"
#include "Domain/Exceptions.h"
namespace Interlude namespace Interlude
{ {
@ -72,73 +74,79 @@ namespace Interlude
private: private:
const Data GetData(const User* item) const const Data GetData(const User* item) const
{ {
const auto playerController = item->pawn ? item->pawn->lineagePlayerController : nullptr; if (!item->pawn) {
throw RuntimeException(std::format(L"pawn is empty for hero {}", item->nickname));
}
const auto playerController = item->pawn->lineagePlayerController;
if (!playerController) {
throw RuntimeException(std::format(L"player controller is empty for hero {}", item->nickname));
}
return { return {
item->objectId, item->objectId,
ValueObjects::Transform( ValueObjects::Transform(
ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z),
ValueObjects::Vector3( ValueObjects::Vector3(
static_cast<float_t>(item->pawn->Rotation.Pitch), static_cast<float_t>(item->pawn->Rotation.Pitch),
static_cast<float_t>(item->pawn->Rotation.Yaw), static_cast<float_t>(item->pawn->Rotation.Yaw),
static_cast<float_t>(item->pawn->Rotation.Roll) static_cast<float_t>(item->pawn->Rotation.Roll)
),
ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z),
ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z)
), ),
ValueObjects::FullName( ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z),
std::wstring(item->nickname), ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z)
std::wstring(item->title) ),
), ValueObjects::FullName(
ValueObjects::VitalStats( std::wstring(item->nickname),
item->maxHp, item->hp, std::wstring(item->title)
item->maxMp, item->mp, ),
item->maxCp, item->cp ValueObjects::VitalStats(
), item->maxHp, item->hp,
ValueObjects::Phenotype( item->maxMp, item->mp,
(Enums::RaceEnum)item->raceId, item->maxCp, item->cp
item->gender == L2::Gender::MALE, ),
(Enums::ClassEnum)item->classId, ValueObjects::Phenotype(
(Enums::ClassEnum)item->activeClassId (Enums::RaceEnum)item->raceId,
), item->gender == L2::Gender::MALE,
ValueObjects::ExperienceInfo( (Enums::ClassEnum)item->classId,
item->lvl, (Enums::ClassEnum)item->activeClassId
item->exp, ),
item->sp ValueObjects::ExperienceInfo(
), item->lvl,
ValueObjects::PermanentStats( item->exp,
item->str, item->sp
item->dex, ),
item->con, ValueObjects::PermanentStats(
item->int_, item->str,
item->men, item->dex,
item->wit item->con,
), item->int_,
ValueObjects::VariableStats( item->men,
item->accuracy, item->wit
item->critRate, ),
item->pAttack, ValueObjects::VariableStats(
item->attackSpeed, item->accuracy,
item->pDefense, item->critRate,
item->evasion, item->pAttack,
item->mAttack, item->attackSpeed,
item->mDefense, item->pDefense,
item->castingSpeed item->evasion,
), item->mAttack,
ValueObjects::Reputation( item->mDefense,
item->karma, item->castingSpeed
item->pkKills, ),
item->pvpKills, ValueObjects::Reputation(
static_cast<uint8_t>(item->recRemaining), item->karma,
static_cast<uint8_t>(item->evalScore) item->pkKills,
), item->pvpKills,
ValueObjects::InventoryInfo( static_cast<uint8_t>(item->recRemaining),
item->maxWeight, static_cast<uint8_t>(item->evalScore)
item->weight, ),
item->invSlotCount ValueObjects::InventoryInfo(
), item->maxWeight,
playerController ? playerController->targetObjectId : 0, item->weight,
playerController ? playerController->isStanding == 1 : true item->invSlotCount
),
playerController->targetObjectId,
playerController->isStanding == 1
}; };
} }
}; };

View File

@ -12,6 +12,7 @@
#include "Domain/Entities/ShieldItem.h" #include "Domain/Entities/ShieldItem.h"
#include "Domain/DTO/ItemData.h" #include "Domain/DTO/ItemData.h"
#include "../Helpers/EnchantHelper.h" #include "../Helpers/EnchantHelper.h"
#include "Domain/Exceptions.h"
using namespace L2Bot::Domain; using namespace L2Bot::Domain;
@ -92,39 +93,37 @@ namespace Interlude
{ {
//FIXME during first start data may be undefined //FIXME during first start data may be undefined
const auto data = GetItemData(itemInfo.itemId); const auto data = GetItemData(itemInfo.itemId);
if (!data) {
if (data) throw RuntimeException(std::format(L"cannot load ItemData for item {}", itemInfo.itemId));
}
switch (data->dataType)
{ {
switch (data->dataType) case L2::ItemDataType::ARMOR:
{ return CreateArmor(itemInfo);
case L2::ItemDataType::ARMOR: case L2::ItemDataType::WEAPON:
return CreateArmor(itemInfo); return CreateWeaponOrShield(itemInfo, static_cast<const FL2WeaponItemData*>(data));
case L2::ItemDataType::WEAPON:
return CreateWeaponOrShield(itemInfo);
}
return CreateEtc(itemInfo);
} }
return nullptr; return CreateEtc(itemInfo);
} }
void Update(std::shared_ptr<Entities::BaseItem>& item, const DTO::ItemData& itemInfo) const void Update(std::shared_ptr<Entities::BaseItem>& item, const DTO::ItemData& itemInfo) const
{ {
//FIXME during first start data may be undefined //FIXME during first start data may be undefined
const auto data = GetItemData(itemInfo.itemId); const auto data = GetItemData(itemInfo.itemId);
if (!data) {
if (data) throw RuntimeException(std::format(L"cannot load ItemData for item {}", itemInfo.itemId));
}
switch (data->dataType)
{ {
switch (data->dataType) case L2::ItemDataType::ARMOR:
{ UpdateArmor(item, itemInfo);
case L2::ItemDataType::ARMOR: return;
UpdateArmor(item, itemInfo); case L2::ItemDataType::WEAPON:
return; UpdateWeaponOrShield(item, itemInfo, static_cast<const FL2WeaponItemData*>(data));
case L2::ItemDataType::WEAPON: return;
UpdateWeaponOrShield(item, itemInfo);
return;
}
} }
UpdateEtc(item, itemInfo); UpdateEtc(item, itemInfo);
@ -215,10 +214,8 @@ namespace Interlude
); );
} }
std::shared_ptr<Entities::BaseItem> CreateWeaponOrShield(const DTO::ItemData& itemInfo) const std::shared_ptr<Entities::BaseItem> CreateWeaponOrShield(const DTO::ItemData& itemInfo, const FL2WeaponItemData* itemData) const
{ {
const auto itemData = static_cast<const FL2WeaponItemData*>(GetItemData(itemInfo.itemId));
if (itemData->weaponType != L2::WeaponType::SHIELD) if (itemData->weaponType != L2::WeaponType::SHIELD)
{ {
const auto& data = GetWeaponData(itemInfo, itemData); const auto& data = GetWeaponData(itemInfo, itemData);
@ -266,10 +263,8 @@ namespace Interlude
); );
} }
void UpdateWeaponOrShield(std::shared_ptr<Entities::BaseItem>& item, const DTO::ItemData& itemInfo) const void UpdateWeaponOrShield(std::shared_ptr<Entities::BaseItem>& item, const DTO::ItemData& itemInfo, const FL2WeaponItemData* itemData) const
{ {
const auto itemData = static_cast<const FL2WeaponItemData*>(GetItemData(itemInfo.itemId));
if (itemData->weaponType != L2::WeaponType::SHIELD) if (itemData->weaponType != L2::WeaponType::SHIELD)
{ {
auto weaponItem = std::dynamic_pointer_cast<Entities::WeaponItem>(item); auto weaponItem = std::dynamic_pointer_cast<Entities::WeaponItem>(item);
@ -322,21 +317,20 @@ namespace Interlude
const BaseData GetBaseData(const DTO::ItemData& itemInfo) const const BaseData GetBaseData(const DTO::ItemData& itemInfo) const
{ {
const auto data = GetItemData(itemInfo.itemId); const auto data = GetItemData(itemInfo.itemId);
if (!data) {
const auto nameEntry = data ? m_FName.GetEntry(data->nameIndex) : nullptr; throw RuntimeException(std::format(L"cannot load ItemData for item {}", itemInfo.itemId));
const auto name = nameEntry ? std::wstring(nameEntry->value) : L""; }
const auto iconEntry = data ? m_FName.GetEntry(data->iconNameIndex) : nullptr; const auto nameEntry = m_FName.GetEntry(data->nameIndex);
const auto icon = iconEntry ? std::wstring(iconEntry->value) : L""; const auto iconEntry = m_FName.GetEntry(data->iconNameIndex);
const auto description = data && data->description ? std::wstring(data->description) : L"";
return { return {
itemInfo.objectId, itemInfo.objectId,
itemInfo.itemId, itemInfo.itemId,
itemInfo.mana, itemInfo.mana,
name, nameEntry ? std::wstring(nameEntry->value) : L"",
icon, iconEntry ? std::wstring(iconEntry->value) : L"",
description, data->description ? std::wstring(data->description) : L"",
(uint16_t)(data ? data->weight : 0) static_cast<uint16_t>(data->weight)
}; };
} }

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <format>
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/NPC.h" #include "Domain/Entities/NPC.h"
#include "Domain/Exceptions.h"
namespace Interlude namespace Interlude
{ {
@ -53,6 +55,9 @@ namespace Interlude
private: private:
const Data GetData(const User* item) const const Data GetData(const User* item) const
{ {
if (!item->pawn) {
throw RuntimeException(std::format(L"pawn is empty for npc {}", item->nickname));
}
return { return {
item->objectId, item->objectId,
ValueObjects::Transform( ValueObjects::Transform(

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <format>
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/Player.h" #include "Domain/Entities/Player.h"
#include "Domain/Exceptions.h"
namespace Interlude namespace Interlude
{ {
@ -50,6 +52,9 @@ namespace Interlude
private: private:
const Data GetData(const User* item) const const Data GetData(const User* item) const
{ {
if (!item->pawn) {
throw RuntimeException(std::format(L"pawn is empty for player {}", item->nickname));
}
return { return {
item->objectId, item->objectId,
ValueObjects::Transform( ValueObjects::Transform(

View File

@ -7,6 +7,7 @@
#include "../GameStructs/FName.h" #include "../GameStructs/FName.h"
#include "../../../Common/Common.h" #include "../../../Common/Common.h"
#include "Domain/Entities/Skill.h" #include "Domain/Entities/Skill.h"
#include "Domain/Exceptions.h"
using namespace L2Bot::Domain; using namespace L2Bot::Domain;
@ -72,21 +73,20 @@ namespace Interlude
const Data GetData(const uint32_t skillId, const uint32_t level, const uint32_t isActive) const const Data GetData(const uint32_t skillId, const uint32_t level, const uint32_t isActive) const
{ {
const auto data = m_L2GameData.GetMSData(skillId, level); const auto data = m_L2GameData.GetMSData(skillId, level);
if (!data) {
throw RuntimeException(std::format(L"cannot load MSData for skill {}", skillId));
}
const auto cost = data ? data->mpCost : 0; const auto iconEntry = m_FName.GetEntry(data->iconNameIndex);
const auto range = data ? data->range : 0;
const auto name = data && data->name ? data->name : L"";
const auto description = data && data->description ? data->description : L"";
const auto iconEntry = data ? m_FName.GetEntry(data->iconNameIndex) : nullptr;
return { return {
skillId, skillId,
static_cast<uint8_t>(level), static_cast<uint8_t>(level),
isActive != 1, isActive != 1,
static_cast<uint8_t>(cost), static_cast<uint8_t>(data->mpCost),
static_cast<int16_t>(range), static_cast<int16_t>(data->range),
std::wstring(name), data->name ? std::wstring(data->name) : L"",
std::wstring(description), data->description ? std::wstring(data->description) : L"",
iconEntry ? std::wstring(iconEntry->value) : L"", iconEntry ? std::wstring(iconEntry->value) : L"",
}; };
} }

View File

@ -5,6 +5,7 @@
#include "../Factories/HeroFactory.h" #include "../Factories/HeroFactory.h"
#include "Domain/Events/HeroCreatedEvent.h" #include "Domain/Events/HeroCreatedEvent.h"
#include "Domain/Events/HeroDeletedEvent.h" #include "Domain/Events/HeroDeletedEvent.h"
#include "Domain/Events/CreatureDiedEvent.h"
#include "../GameStructs/NetworkHandlerWrapper.h" #include "../GameStructs/NetworkHandlerWrapper.h"
#include "Domain/Services/ServiceLocator.h" #include "Domain/Services/ServiceLocator.h"
@ -49,6 +50,21 @@ namespace Interlude
m_Hero = nullptr; m_Hero = nullptr;
} }
void Init() override
{
Services::ServiceLocator::GetInstance().GetEventDispatcher()->Subscribe(Events::CreatureDiedEvent::name, [this](const Events::Event& evt) {
std::unique_lock<std::shared_timed_mutex>(m_Mutex);
if (evt.GetName() == Events::CreatureDiedEvent::name)
{
const auto casted = static_cast<const Events::CreatureDiedEvent&>(evt);
if (m_Hero && m_Hero->GetId() == casted.GetCreatureId())
{
Services::ServiceLocator::GetInstance().GetLogger()->App(L"{} died", m_Hero->GetFullName().GetNickname());
}
}
});
}
HeroRepository(const NetworkHandlerWrapper& networkHandler, const HeroFactory& factory) : HeroRepository(const NetworkHandlerWrapper& networkHandler, const HeroFactory& factory) :
m_NetworkHandler(networkHandler), m_NetworkHandler(networkHandler),
m_Factory(factory) m_Factory(factory)