From 521af7c00eaa157aecbe664fa376f5b6e9b6be88 Mon Sep 17 00:00:00 2001 From: k0t9i Date: Wed, 18 Oct 2023 13:28:29 +0400 Subject: [PATCH] feat: add exceptions --- L2BotCore/Domain/Exceptions.h | 62 ++++++++ L2BotCore/Domain/Logger/LogChannel.h | 2 +- L2BotCore/Domain/Logger/Logger.h | 23 ++- L2BotCore/Domain/Services/WorldHandler.h | 75 +++++++--- L2BotCore/L2BotCore.vcxproj | 1 + L2BotCore/L2BotCore.vcxproj.filters | 3 + L2BotDll/Transports/NamedPipe.h | 22 ++- .../Factories/AbnormalEffectFactory.h | 13 +- .../Interlude/Factories/DropFactory.h | 13 +- .../Interlude/Factories/HeroFactory.h | 134 ++++++++++-------- .../Interlude/Factories/ItemFactory.h | 72 +++++----- .../Versions/Interlude/Factories/NPCFactory.h | 5 + .../Interlude/Factories/PlayerFactory.h | 5 + .../Interlude/Factories/SkillFactory.h | 18 +-- .../Interlude/Repositories/HeroRepository.h | 16 +++ 15 files changed, 313 insertions(+), 151 deletions(-) create mode 100644 L2BotCore/Domain/Exceptions.h diff --git a/L2BotCore/Domain/Exceptions.h b/L2BotCore/Domain/Exceptions.h new file mode 100644 index 0000000..9762aca --- /dev/null +++ b/L2BotCore/Domain/Exceptions.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +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) {} + }; + +} \ No newline at end of file diff --git a/L2BotCore/Domain/Logger/LogChannel.h b/L2BotCore/Domain/Logger/LogChannel.h index 436bf20..ea8ddef 100644 --- a/L2BotCore/Domain/Logger/LogChannel.h +++ b/L2BotCore/Domain/Logger/LogChannel.h @@ -33,7 +33,7 @@ namespace L2Bot::Domain::Logger struct tm timeinfo; localtime_s(&timeinfo, &rawTime); 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(); } diff --git a/L2BotCore/Domain/Logger/Logger.h b/L2BotCore/Domain/Logger/Logger.h index ac3bf90..edf53cc 100644 --- a/L2BotCore/Domain/Logger/Logger.h +++ b/L2BotCore/Domain/Logger/Logger.h @@ -35,9 +35,30 @@ namespace L2Bot::Domain::Logger 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: template void Log(LogLevel level, const std::wformat_string 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""; if (level == LogLevel::error) { @@ -55,7 +76,7 @@ namespace L2Bot::Domain::Logger for (const auto& channel : m_Channels) { if (channel->IsAppropriateLevel(level)) { - channel->SendToChannel(prefix + std::vformat(format.get(), std::make_wformat_args(args...))); + channel->SendToChannel(prefix + message); } } } diff --git a/L2BotCore/Domain/Services/WorldHandler.h b/L2BotCore/Domain/Services/WorldHandler.h index b8ef7f9..abf14ae 100644 --- a/L2BotCore/Domain/Services/WorldHandler.h +++ b/L2BotCore/Domain/Services/WorldHandler.h @@ -10,6 +10,8 @@ #include "../DTO/Message.h" #include "../Services/IncomingMessageProcessor.h" #include "../Services/OutgoingMessageBuilder.h" +#include "../Exceptions.h" +#include "../Services/ServiceLocator.h" namespace L2Bot::Domain::Services { @@ -69,19 +71,30 @@ namespace L2Bot::Domain::Services { while (!m_Stopped) { - const auto& messages = GetOutgoingMessages(); + try { + const auto& messages = GetOutgoingMessages(); - if (m_Transport.IsConnected()) - { - for (const auto& message : messages) + if (m_Transport.IsConnected()) { - m_Transport.Send( - m_Serializer.Serialize(message) - ); + for (const auto& message : messages) + { + 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) { - if (m_Transport.IsConnected()) - { - const auto messageType = m_IncomingMessageProcessor.Process(m_Transport.Receive()); + try { + if (m_Transport.IsConnected()) + { + 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) { - Invalidate(); + if (messageType == Serializers::IncomingMessage::Type::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) { - if (!m_Transport.IsConnected()) - { - m_Transport.Connect(); + try { + if (!m_Transport.IsConnected()) + { + 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)); } } diff --git a/L2BotCore/L2BotCore.vcxproj b/L2BotCore/L2BotCore.vcxproj index c3da13a..c22d1eb 100644 --- a/L2BotCore/L2BotCore.vcxproj +++ b/L2BotCore/L2BotCore.vcxproj @@ -229,6 +229,7 @@ + diff --git a/L2BotCore/L2BotCore.vcxproj.filters b/L2BotCore/L2BotCore.vcxproj.filters index ea47703..3aee58f 100644 --- a/L2BotCore/L2BotCore.vcxproj.filters +++ b/L2BotCore/L2BotCore.vcxproj.filters @@ -234,6 +234,9 @@ Header Files + + Header Files + diff --git a/L2BotDll/Transports/NamedPipe.h b/L2BotDll/Transports/NamedPipe.h index 936c2b2..b731dfe 100644 --- a/L2BotDll/Transports/NamedPipe.h +++ b/L2BotDll/Transports/NamedPipe.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include "Domain/Exceptions.h" #include "Domain/Services/ServiceLocator.h" #define BUFFER_SIZE 16384 @@ -38,8 +40,7 @@ public: if (m_Pipe == INVALID_HANDLE_VALUE) { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot connect to the pipe ""{}""", m_PipeName); - return false; + throw RuntimeException(std::format(L"cannot create the pipe {}: {}", m_PipeName, GetLastError())); } } else @@ -81,14 +82,14 @@ public: const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_WritingOverlapped, &ret, false); if (!overlappedResult) { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot get overlapped result for the pipe ""{}"" when writing", m_PipeName); m_Connected = false; + throw RuntimeException(std::format(L"cannot get overlapped result for the pipe {} when writing", m_PipeName)); } } else { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot write to the pipe ""{}"": {}", m_PipeName, lastError); 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); if (!overlappedResult) { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot get overlapped result for the pipe ""{}"" when reading", m_PipeName); m_Connected = false; - return L""; + throw RuntimeException(std::format(L"cannot get overlapped result for the pipe {} when reading", m_PipeName)); } } else { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot read from the pipe ""{}"": {}", m_PipeName, lastError); 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; 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()) @@ -165,7 +164,7 @@ private: if (SetEvent(m_ConntectingOverlapped.hEvent)) break; 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); if (overlapped.hEvent == NULL) { - Services::ServiceLocator::GetInstance().GetLogger()->Error(L"cannot create overlapped for the pipe ""{}"": {}", m_PipeName, GetLastError()); - return; + throw RuntimeException(std::format(L"cannot create overlapped for the pipe {}: {}", m_PipeName, GetLastError())); } overlapped.Offset = 0; overlapped.OffsetHigh = 0; diff --git a/L2BotDll/Versions/Interlude/Factories/AbnormalEffectFactory.h b/L2BotDll/Versions/Interlude/Factories/AbnormalEffectFactory.h index cdf18b8..81a2c0b 100644 --- a/L2BotDll/Versions/Interlude/Factories/AbnormalEffectFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/AbnormalEffectFactory.h @@ -7,6 +7,7 @@ #include "../GameStructs/FName.h" #include "../../../Common/Common.h" #include "Domain/Entities/AbnormalEffect.h" +#include "Domain/Exceptions.h" using namespace L2Bot::Domain; @@ -28,15 +29,17 @@ namespace Interlude { const auto data = m_L2GameData.GetMSData(skillId, level); - 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; + if (!data) { + throw RuntimeException(std::format(L"cannot load MSData for abnormal effect {}", skillId)); + } + + const auto iconEntry = m_FName.GetEntry(data->iconNameIndex); return std::make_shared( skillId, static_cast(level), - std::wstring(name), - std::wstring(description), + data->name ? std::wstring(data->name) : L"", + data->description ? std::wstring(data->description) : L"", iconEntry ? std::wstring(iconEntry->value) : L"" ); } diff --git a/L2BotDll/Versions/Interlude/Factories/DropFactory.h b/L2BotDll/Versions/Interlude/Factories/DropFactory.h index f038d8a..55e3b97 100644 --- a/L2BotDll/Versions/Interlude/Factories/DropFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/DropFactory.h @@ -1,11 +1,13 @@ #pragma once #include +#include #include "../GameStructs/L2GameDataWrapper.h" #include "../GameStructs/FName.h" #include "../GameStructs/GameStructs.h" #include "../../../Common/Common.h" #include "Domain/Entities/Drop.h" +#include "Domain/Exceptions.h" namespace Interlude { @@ -63,8 +65,15 @@ namespace Interlude const Data GetData(const Item* item) const { const auto itemData = m_L2GameData.GetItemData(item->itemId); - const auto nameEntry = itemData ? m_FName.GetEntry(itemData->nameIndex) : nullptr; - const auto iconEntry = itemData ? m_FName.GetEntry(itemData->iconNameIndex) : nullptr; + if (!itemData) { + 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 { item->objectId, diff --git a/L2BotDll/Versions/Interlude/Factories/HeroFactory.h b/L2BotDll/Versions/Interlude/Factories/HeroFactory.h index 3db48fc..85fa0fb 100644 --- a/L2BotDll/Versions/Interlude/Factories/HeroFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/HeroFactory.h @@ -1,9 +1,11 @@ #pragma once #include +#include #include "../GameStructs/GameStructs.h" #include "../../../Common/Common.h" #include "Domain/Entities/Hero.h" +#include "Domain/Exceptions.h" namespace Interlude { @@ -72,73 +74,79 @@ namespace Interlude private: 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 { item->objectId, - ValueObjects::Transform( - ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), - ValueObjects::Vector3( - static_cast(item->pawn->Rotation.Pitch), - static_cast(item->pawn->Rotation.Yaw), - static_cast(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::Transform( + ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), + ValueObjects::Vector3( + static_cast(item->pawn->Rotation.Pitch), + static_cast(item->pawn->Rotation.Yaw), + static_cast(item->pawn->Rotation.Roll) ), - ValueObjects::FullName( - std::wstring(item->nickname), - std::wstring(item->title) - ), - ValueObjects::VitalStats( - item->maxHp, item->hp, - item->maxMp, item->mp, - item->maxCp, item->cp - ), - ValueObjects::Phenotype( - (Enums::RaceEnum)item->raceId, - item->gender == L2::Gender::MALE, - (Enums::ClassEnum)item->classId, - (Enums::ClassEnum)item->activeClassId - ), - ValueObjects::ExperienceInfo( - item->lvl, - item->exp, - item->sp - ), - ValueObjects::PermanentStats( - item->str, - item->dex, - item->con, - item->int_, - item->men, - item->wit - ), - ValueObjects::VariableStats( - item->accuracy, - item->critRate, - item->pAttack, - item->attackSpeed, - item->pDefense, - item->evasion, - item->mAttack, - item->mDefense, - item->castingSpeed - ), - ValueObjects::Reputation( - item->karma, - item->pkKills, - item->pvpKills, - static_cast(item->recRemaining), - static_cast(item->evalScore) - ), - ValueObjects::InventoryInfo( - item->maxWeight, - item->weight, - item->invSlotCount - ), - playerController ? playerController->targetObjectId : 0, - playerController ? playerController->isStanding == 1 : true + 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( + std::wstring(item->nickname), + std::wstring(item->title) + ), + ValueObjects::VitalStats( + item->maxHp, item->hp, + item->maxMp, item->mp, + item->maxCp, item->cp + ), + ValueObjects::Phenotype( + (Enums::RaceEnum)item->raceId, + item->gender == L2::Gender::MALE, + (Enums::ClassEnum)item->classId, + (Enums::ClassEnum)item->activeClassId + ), + ValueObjects::ExperienceInfo( + item->lvl, + item->exp, + item->sp + ), + ValueObjects::PermanentStats( + item->str, + item->dex, + item->con, + item->int_, + item->men, + item->wit + ), + ValueObjects::VariableStats( + item->accuracy, + item->critRate, + item->pAttack, + item->attackSpeed, + item->pDefense, + item->evasion, + item->mAttack, + item->mDefense, + item->castingSpeed + ), + ValueObjects::Reputation( + item->karma, + item->pkKills, + item->pvpKills, + static_cast(item->recRemaining), + static_cast(item->evalScore) + ), + ValueObjects::InventoryInfo( + item->maxWeight, + item->weight, + item->invSlotCount + ), + playerController->targetObjectId, + playerController->isStanding == 1 }; } }; diff --git a/L2BotDll/Versions/Interlude/Factories/ItemFactory.h b/L2BotDll/Versions/Interlude/Factories/ItemFactory.h index 2b27f40..a1806c2 100644 --- a/L2BotDll/Versions/Interlude/Factories/ItemFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/ItemFactory.h @@ -12,6 +12,7 @@ #include "Domain/Entities/ShieldItem.h" #include "Domain/DTO/ItemData.h" #include "../Helpers/EnchantHelper.h" +#include "Domain/Exceptions.h" using namespace L2Bot::Domain; @@ -92,39 +93,37 @@ namespace Interlude { //FIXME during first start data may be undefined 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::WEAPON: - return CreateWeaponOrShield(itemInfo); - } - - return CreateEtc(itemInfo); + case L2::ItemDataType::ARMOR: + return CreateArmor(itemInfo); + case L2::ItemDataType::WEAPON: + return CreateWeaponOrShield(itemInfo, static_cast(data)); } - return nullptr; + return CreateEtc(itemInfo); } void Update(std::shared_ptr& item, const DTO::ItemData& itemInfo) const { //FIXME during first start data may be undefined 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); - return; - case L2::ItemDataType::WEAPON: - UpdateWeaponOrShield(item, itemInfo); - return; - } + case L2::ItemDataType::ARMOR: + UpdateArmor(item, itemInfo); + return; + case L2::ItemDataType::WEAPON: + UpdateWeaponOrShield(item, itemInfo, static_cast(data)); + return; } UpdateEtc(item, itemInfo); @@ -215,10 +214,8 @@ namespace Interlude ); } - std::shared_ptr CreateWeaponOrShield(const DTO::ItemData& itemInfo) const + std::shared_ptr CreateWeaponOrShield(const DTO::ItemData& itemInfo, const FL2WeaponItemData* itemData) const { - const auto itemData = static_cast(GetItemData(itemInfo.itemId)); - if (itemData->weaponType != L2::WeaponType::SHIELD) { const auto& data = GetWeaponData(itemInfo, itemData); @@ -266,10 +263,8 @@ namespace Interlude ); } - void UpdateWeaponOrShield(std::shared_ptr& item, const DTO::ItemData& itemInfo) const + void UpdateWeaponOrShield(std::shared_ptr& item, const DTO::ItemData& itemInfo, const FL2WeaponItemData* itemData) const { - const auto itemData = static_cast(GetItemData(itemInfo.itemId)); - if (itemData->weaponType != L2::WeaponType::SHIELD) { auto weaponItem = std::dynamic_pointer_cast(item); @@ -322,21 +317,20 @@ namespace Interlude const BaseData GetBaseData(const DTO::ItemData& itemInfo) const { const auto data = GetItemData(itemInfo.itemId); - - const auto nameEntry = data ? m_FName.GetEntry(data->nameIndex) : nullptr; - const auto name = nameEntry ? std::wstring(nameEntry->value) : L""; - const auto iconEntry = data ? m_FName.GetEntry(data->iconNameIndex) : nullptr; - const auto icon = iconEntry ? std::wstring(iconEntry->value) : L""; - const auto description = data && data->description ? std::wstring(data->description) : L""; + if (!data) { + throw RuntimeException(std::format(L"cannot load ItemData for item {}", itemInfo.itemId)); + } + const auto nameEntry = m_FName.GetEntry(data->nameIndex); + const auto iconEntry = m_FName.GetEntry(data->iconNameIndex); return { itemInfo.objectId, itemInfo.itemId, itemInfo.mana, - name, - icon, - description, - (uint16_t)(data ? data->weight : 0) + nameEntry ? std::wstring(nameEntry->value) : L"", + iconEntry ? std::wstring(iconEntry->value) : L"", + data->description ? std::wstring(data->description) : L"", + static_cast(data->weight) }; } diff --git a/L2BotDll/Versions/Interlude/Factories/NPCFactory.h b/L2BotDll/Versions/Interlude/Factories/NPCFactory.h index 5d9f60e..15c576a 100644 --- a/L2BotDll/Versions/Interlude/Factories/NPCFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/NPCFactory.h @@ -1,8 +1,10 @@ #pragma once #include +#include #include "../../../Common/Common.h" #include "Domain/Entities/NPC.h" +#include "Domain/Exceptions.h" namespace Interlude { @@ -53,6 +55,9 @@ namespace Interlude private: const Data GetData(const User* item) const { + if (!item->pawn) { + throw RuntimeException(std::format(L"pawn is empty for npc {}", item->nickname)); + } return { item->objectId, ValueObjects::Transform( diff --git a/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h b/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h index 4c4f2ae..d2598b4 100644 --- a/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h @@ -1,8 +1,10 @@ #pragma once #include +#include #include "../../../Common/Common.h" #include "Domain/Entities/Player.h" +#include "Domain/Exceptions.h" namespace Interlude { @@ -50,6 +52,9 @@ namespace Interlude private: const Data GetData(const User* item) const { + if (!item->pawn) { + throw RuntimeException(std::format(L"pawn is empty for player {}", item->nickname)); + } return { item->objectId, ValueObjects::Transform( diff --git a/L2BotDll/Versions/Interlude/Factories/SkillFactory.h b/L2BotDll/Versions/Interlude/Factories/SkillFactory.h index c102da4..c60069c 100644 --- a/L2BotDll/Versions/Interlude/Factories/SkillFactory.h +++ b/L2BotDll/Versions/Interlude/Factories/SkillFactory.h @@ -7,6 +7,7 @@ #include "../GameStructs/FName.h" #include "../../../Common/Common.h" #include "Domain/Entities/Skill.h" +#include "Domain/Exceptions.h" 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 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 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; + const auto iconEntry = m_FName.GetEntry(data->iconNameIndex); return { skillId, static_cast(level), isActive != 1, - static_cast(cost), - static_cast(range), - std::wstring(name), - std::wstring(description), + static_cast(data->mpCost), + static_cast(data->range), + data->name ? std::wstring(data->name) : L"", + data->description ? std::wstring(data->description) : L"", iconEntry ? std::wstring(iconEntry->value) : L"", }; } diff --git a/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h b/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h index 95c097e..122dd26 100644 --- a/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h +++ b/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h @@ -5,6 +5,7 @@ #include "../Factories/HeroFactory.h" #include "Domain/Events/HeroCreatedEvent.h" #include "Domain/Events/HeroDeletedEvent.h" +#include "Domain/Events/CreatureDiedEvent.h" #include "../GameStructs/NetworkHandlerWrapper.h" #include "Domain/Services/ServiceLocator.h" @@ -49,6 +50,21 @@ namespace Interlude m_Hero = nullptr; } + void Init() override + { + Services::ServiceLocator::GetInstance().GetEventDispatcher()->Subscribe(Events::CreatureDiedEvent::name, [this](const Events::Event& evt) { + std::unique_lock(m_Mutex); + if (evt.GetName() == Events::CreatureDiedEvent::name) + { + const auto casted = static_cast(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) : m_NetworkHandler(networkHandler), m_Factory(factory)