From 6cd1310460ac702791cc5bd883635c69edc00eb9 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Wed, 8 Jan 2025 14:01:09 -0600 Subject: [PATCH 1/9] First pass --- dGame/dUtilities/Mail.cpp | 87 ++--------- dGame/dUtilities/Mail.h | 282 ++++++++++++++++++++++++++++++++--- dNet/BitStreamUtils.h | 63 +++++++- dWorldServer/WorldServer.cpp | 2 +- 4 files changed, 336 insertions(+), 98 deletions(-) diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index e2ffbf79f..f333851d0 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -94,7 +94,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ SendNotification(sysAddr, 1); //Show the "one new mail" message } -void Mail::HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) { +void Mail::HandleMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) { int mailStuffID = 0; packet.Read(mailStuffID); @@ -142,7 +142,7 @@ void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAdd u"This character has restricted mail access." ); - Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::AccountIsMuted); + Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::SenderAccountIsMuted); return; } @@ -243,50 +243,9 @@ void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAdd void Mail::HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player) { auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20); - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::MailData)); - bitStream.Write(int(0)); // throttled - - bitStream.Write(playerMail.size()); // size - bitStream.Write(0); - - for (const auto& mail : playerMail) { - bitStream.Write(mail.id); //MailID - - const LUWString subject(mail.subject, 50); - bitStream.Write(subject); //subject - const LUWString body(mail.body, 400); - bitStream.Write(body); //body - const LUWString sender(mail.senderUsername, 32); - bitStream.Write(sender); //sender - bitStream.Write(uint32_t(0)); // packing - - bitStream.Write(uint64_t(0)); // attachedCurrency - - bitStream.Write(mail.itemID); //Attachment ID - LOT lot = mail.itemLOT; - if (lot <= 0) bitStream.Write(LOT(-1)); - else bitStream.Write(lot); - bitStream.Write(uint32_t(0)); // packing - - bitStream.Write(mail.itemSubkey); // Attachment subKey - - bitStream.Write(mail.itemCount); // Attachment count - bitStream.Write(uint8_t(0)); // subject type (used for auction) - bitStream.Write(uint8_t(0)); // packing - bitStream.Write(uint32_t(0)); // packing - - bitStream.Write(mail.timeSent); // expiration date - bitStream.Write(mail.timeSent);// send date - bitStream.Write(mail.wasRead); //was read - - bitStream.Write(uint8_t(0)); // isLocalized - bitStream.Write(uint16_t(0)); // packing - bitStream.Write(uint32_t(0)); // packing - } - + Mail::Data data = Mail::Data(playerMail); + bitStream.Write(data); Game::server->Send(bitStream, sysAddr, false); } @@ -348,46 +307,28 @@ void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t obje void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::SendResponse)); - bitStream.Write(int(response)); + Mail::Response data = Mail::Response(response); + bitStream.Write(data); Game::server->Send(bitStream, sysAddr, false); } void Mail::SendNotification(const SystemAddress& sysAddr, int mailCount) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - uint64_t messageType = 2; - uint64_t s1 = 0; - uint64_t s2 = 0; - uint64_t s3 = 0; - uint64_t s4 = 0; - - bitStream.Write(messageType); - bitStream.Write(s1); - bitStream.Write(s2); - bitStream.Write(s3); - bitStream.Write(s4); - bitStream.Write(mailCount); - bitStream.Write(int(0)); //Unknown + Mail::Notification data = Mail::Notification(MailNotification::NewMail, mailCount); Game::server->Send(bitStream, sysAddr, false); } void Mail::SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::AttachmentCollectConfirm)); - bitStream.Write(int(0)); //unknown - bitStream.Write(mailID); + AttachmentCollect data = AttachmentCollect(MailRemoveAttachment::Success, mailID); + bitStream.Write(data); Game::server->Send(bitStream, sysAddr, false); } void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::MailDeleteConfirm)); - bitStream.Write(int(0)); //unknown - bitStream.Write(mailID); + DeleteMail data = DeleteMail(MailDeleteResponse::Success, mailID); + bitStream.Write(data); Game::server->Send(bitStream, sysAddr, false); Database::Get()->DeleteMail(mailID); @@ -395,10 +336,8 @@ void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOO void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) { RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::MailReadConfirm)); - bitStream.Write(int(0)); //unknown - bitStream.Write(mailID); + Read data = Read(MailReadResponse::Success, mailID); + bitStream.Write(data); Game::server->Send(bitStream, sysAddr, false); Database::Get()->MarkMailRead(mailID); diff --git a/dGame/dUtilities/Mail.h b/dGame/dUtilities/Mail.h index 07c3e37fb..6a78f47a0 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -1,40 +1,185 @@ -#pragma once +#ifndef __MAIL_H__ +#define __MAIL_H__ + +#include #include "BitStream.h" #include "RakNetTypes.h" #include "dCommonVars.h" +template +struct LUHeader; + class Entity; namespace Mail { - enum class MailMessageID { - Send = 0x00, - SendResponse = 0x01, - DataRequest = 0x03, - MailData = 0x04, - AttachmentCollect = 0x05, - AttachmentCollectConfirm = 0x06, - MailDelete = 0x07, - MailDeleteConfirm = 0x08, - MailRead = 0x09, - MailReadConfirm = 0x0a, - NotificationRequest = 0x0b + enum class MailMessageID : uint32_t { + Send = 0, + SendResponse, + Notification, + DataRequest, + MailData, + AttachmentCollect, + AttachmentCollectConfirm, + MailDelete, + MailDeleteConfirm, + MailRead, + MailReadConfirm, + NotificationRequest, + AuctionCreate, + AuctionCreationResponse, + AuctionCancel, + AuctionCancelResponse, + AuctionList, + AuctionListResponse, + AuctionBid, + AuctionBidResponse, + UnknownError }; - enum class MailSendResponse { + enum class MailSendResponse : uint32_t { Success = 0, NotEnoughCoins, AttachmentNotFound, ItemCannotBeMailed, CannotMailSelf, RecipientNotFound, - DifferentFaction, - Unknown, + RecipientDifferentFaction, + UnHandled7, ModerationFailure, - AccountIsMuted, - UnknownFailure, + SenderAccountIsMuted, + UnHandled10, RecipientIsIgnored, - UnknownFailure3, - RecipientIsFTP + UnHandled12, + RecipientIsFTP, + UnknownError + }; + + enum class MailDeleteResponse : uint32_t { + Success = 0, + HasAttachements, + NotFoud, + Throttled, + UnknownError + }; + + enum class MailRemoveAttachment : uint32_t { + Success = 0, + AttachmentNotFound, + NoSpaceInInventory, + MailNotFound, + Throttled, + UnknownError + }; + + enum class MailNotification : uint32_t { + NewMail = 0, + UnHandled, + AuctionWon, + AuctionSold, + AuctionOutbided, + AuctionExpired, + AuctionCancelled, + AuctionUpdated, + UnknownError + }; + + enum class MailReadResponse : uint32_t { + Success = 0, + UnknownError + }; + + struct MailInfo { + uint64_t id; + std::string senderUsername; + std::string recipient; + std::string subject; + std::string body; + uint32_t senderId; + uint32_t receiverId; + uint32_t itemCount; + uint32_t itemID; + LOT itemLOT; + LWOOBJID itemSubkey; + }; + + struct MailLUHeader { + LUHeader header = LUHeader(eConnectionType::CLIENT, MessageType::Client::MAIL); + MailMessageID messageID; + }; + + struct Data { + MailLUHeader header; + uint32_t throttled = 0; + std::vector playerMail; + + Data() = delete; + Data(std::vector mailData) { + this->header.messageID = MailMessageID::MailData; + this->playerMail = mailData; + } + }; + + struct Response { + MailLUHeader header; + MailSendResponse response; + + Response() = delete; + Response(MailSendResponse response) { + this->header.messageID = MailMessageID::SendResponse; + this->response = response; + } + }; + + struct Notification { + MailLUHeader header; + MailNotification notification; + LWOOBJID auctionID = 0; + uint32_t mailCount = 0; + + Notification() = delete; + Notification(MailNotification notification, uint32_t mailCount, LWOOBJID auctionID = LWOOBJID_EMPTY) { + this->header.messageID = MailMessageID::Notification; + this->notification = notification; + this->auctionID = auctionID; + this->mailCount = mailCount; + } + }; + + struct AttachmentCollect { + MailLUHeader header; + MailRemoveAttachment status; + uint64_t mailID; + + AttachmentCollect() = delete; + AttachmentCollect(MailRemoveAttachment status, uint64_t mailID) { + this->header.messageID = MailMessageID::AttachmentCollect; + this->status = status; + this->mailID = mailID; + } + }; + + struct DeleteMail { + MailLUHeader header; + MailDeleteResponse status; + uint64_t mailID; + + DeleteMail() = delete; + DeleteMail(MailDeleteResponse status, uint64_t mailID) { + this->status = status; + this->mailID = mailID; + } + }; + + struct Read { + MailLUHeader header; + MailReadResponse status; + uint64_t mailID; + + Read() = delete; + Read(MailReadResponse status, uint64_t mailID) { + this->status = status; + this->mailID = mailID; + } }; const std::string ServerName = "Darkflame Universe"; @@ -79,7 +224,7 @@ namespace Mail { const SystemAddress& sysAddr ); - void HandleMailStuff(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity); + void HandleMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity); void HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity); void HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player); void HandleAttachmentCollect(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player); @@ -93,3 +238,98 @@ namespace Mail { void SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID); void SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID); }; + + +namespace RakNet { + template <> + inline void RakNet::BitStream::Write(Mail::MailLUHeader data) { + this->Write>(data.header); + this->Write(static_cast(data.messageID)); + } + + template <> + inline void RakNet::BitStream::Write(Mail::Data data) { + + this->Write(data.header); + this->Write(data.throttled); + + this->Write(data.playerMail.size()); + this->Write(0); // packing + for (auto& mail : data.playerMail) { + this->Write(mail.id); + + const LUWString subject(mail.subject, 50); + this->Write(subject); + + const LUWString body(mail.body, 400); + this->Write(body); + + const LUWString sender(mail.senderUsername, 32); + this->Write(sender); + this->Write(uint32_t(0)); // packing + + this->Write(uint64_t(0)); // attachedCurrency + this->Write(mail.itemID); + + LOT lot = mail.itemLOT; + if (lot <= 0) this->Write(LOT(-1)); + else this->Write(lot); + this->Write(uint32_t(0)); // packing + + this->Write(mail.itemSubkey); + + this->Write(mail.itemCount); + this->Write(uint8_t(0)); // subject type (used for auction) + this->Write(uint8_t(0)); // packing + this->Write(uint32_t(0)); // packing + + this->Write(mail.timeSent); // expiration date + this->Write(mail.timeSent);// send date + this->Write(mail.wasRead); // was read + + this->Write(uint8_t(0)); // isLocalized + this->Write(uint16_t(0)); // packing + this->Write(uint32_t(0)); // packing + } + } + + template <> + inline void RakNet::BitStream::Write(Mail::Response data) { + this->Write(data.header); + this->Write(static_cast(data.response)); + } + + template <> + inline void RakNet::BitStream::Write(Mail::Notification data) { + this->Write(data.header); + this->Write(data.notification); + this->Write(0); // unused + this->Write(0); // unused + this->Write(data.auctionID); + this->Write(0); // unused + this->Write(data.mailCount); + } + + template <> + inline void RakNet::BitStream::Write(Mail::AttachmentCollect data) { + this->Write(data.header); + this->Write(data.status); + this->Write(data.mailID); + } + + template <> + inline void RakNet::BitStream::Write(Mail::DeleteMail data) { + this->Write(data.header); + this->Write(data.status); + this->Write(data.mailID); + } + + template <> + inline void RakNet::BitStream::Write(Mail::Read data) { + this->Write(data.header); + this->Write(data.status); + this->Write(data.mailID); + } +} + +#endif // !__MAIL_H__ diff --git a/dNet/BitStreamUtils.h b/dNet/BitStreamUtils.h index 33fde5642..d55df14ad 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -2,11 +2,18 @@ #define __BITSTREAMUTILS__H__ #include "GeneralUtils.h" -#include "MessageIdentifiers.h" #include "BitStream.h" #include #include +namespace MessageType { + enum class Auth : uint32_t; + enum class Client : uint32_t; + enum class Server : uint32_t; + enum class World : uint32_t; + enum class Master : uint32_t; +} + enum class eConnectionType : uint16_t; struct LUString { @@ -45,6 +52,19 @@ struct LUWString { }; }; +template +struct LUHeader { + MessageID messageID; + eConnectionType connectionType; + T internalPacketID; + + LUHeader(eConnectionType connectionType, T internalPacketID, MessageID messageID = ID_USER_PACKET_ENUM) { + this->messageID = messageID; + this->connectionType = connectionType; + this->internalPacketID = internalPacketID; + }; +}; + namespace BitStreamUtils { template void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) { @@ -53,7 +73,6 @@ namespace BitStreamUtils { bitStream.Write(static_cast(internalPacketID)); bitStream.Write(0); } - } namespace RakNet { @@ -100,6 +119,46 @@ namespace RakNet { value.string.resize(value.size); this->Write(value.string); } + + template <> + inline void RakNet::BitStream::Write>(LUHeader value) { + this->Write(value.messageID); + this->Write(value.connectionType); + this->Write(static_cast(value.internalPacketID)); + this->Write(0); + } + + template <> + inline void RakNet::BitStream::Write>(LUHeader value) { + this->Write(value.messageID); + this->Write(value.connectionType); + this->Write(static_cast(value.internalPacketID)); + this->Write(0); + } + + template <> + inline void RakNet::BitStream::Write>(LUHeader value) { + this->Write(value.messageID); + this->Write(value.connectionType); + this->Write(static_cast(value.internalPacketID)); + this->Write(0); + } + + template <> + inline void RakNet::BitStream::Write>(LUHeader value) { + this->Write(value.messageID); + this->Write(value.connectionType); + this->Write(static_cast(value.internalPacketID)); + this->Write(0); + } + + template <> + inline void RakNet::BitStream::Write>(LUHeader value) { + this->Write(value.messageID); + this->Write(value.connectionType); + this->Write(static_cast(value.internalPacketID)); + this->Write(0); + } }; #endif //!__BITSTREAMUTILS__H__ diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 6a5969600..a467ac0f1 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1189,7 +1189,7 @@ void HandlePacket(Packet* packet) { // FIXME: Change this to the macro to skip the header... LWOOBJID space; bitStream.Read(space); - Mail::HandleMailStuff(bitStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); + Mail::HandleMail(bitStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); break; } From 7b1d6948c39ae5f4c4b798ed0cc62cca01807b07 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sun, 19 Jan 2025 00:25:20 -0600 Subject: [PATCH 2/9] Overaul, need to test --- dCommon/dEnums/eConnectionType.h | 3 +- dDatabase/GameDatabase/ITables/IMail.h | 23 +- dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 6 +- dDatabase/GameDatabase/MySQL/Tables/Mail.cpp | 13 +- .../GameDatabase/SQLite/SQLiteDatabase.h | 6 +- dDatabase/GameDatabase/SQLite/Tables/Mail.cpp | 12 +- .../GameDatabase/TestSQL/TestSQLDatabase.cpp | 6 +- .../GameDatabase/TestSQL/TestSQLDatabase.h | 6 +- dGame/dComponents/CharacterComponent.cpp | 2 +- dGame/dComponents/InventoryComponent.cpp | 2 +- dGame/dUtilities/Mail.cpp | 525 +++++++++--------- dGame/dUtilities/Mail.h | 304 ++++------ .../GMGreaterThanZeroCommands.cpp | 2 +- dNet/BitStreamUtils.cpp | 29 + dNet/BitStreamUtils.h | 79 +-- dNet/CMakeLists.txt | 1 + dNet/MailInfo.cpp | 87 +++ dNet/MailInfo.h | 33 ++ dWorldServer/WorldServer.cpp | 19 +- 19 files changed, 592 insertions(+), 566 deletions(-) create mode 100644 dNet/BitStreamUtils.cpp create mode 100644 dNet/MailInfo.cpp create mode 100644 dNet/MailInfo.h diff --git a/dCommon/dEnums/eConnectionType.h b/dCommon/dEnums/eConnectionType.h index 406110a9e..ed75e1003 100644 --- a/dCommon/dEnums/eConnectionType.h +++ b/dCommon/dEnums/eConnectionType.h @@ -7,7 +7,8 @@ enum class eConnectionType : uint16_t { CHAT, WORLD = 4, CLIENT, - MASTER + MASTER, + UNKNOWN }; #endif //!__ECONNECTIONTYPE__H__ diff --git a/dDatabase/GameDatabase/ITables/IMail.h b/dDatabase/GameDatabase/ITables/IMail.h index 7fbc8230d..e77f9fa22 100644 --- a/dDatabase/GameDatabase/ITables/IMail.h +++ b/dDatabase/GameDatabase/ITables/IMail.h @@ -8,27 +8,14 @@ #include "dCommonVars.h" #include "NiQuaternion.h" #include "NiPoint3.h" +#include "MailInfo.h" + +namespace RakNet { + class BitStream; +} class IMail { public: - struct MailInfo { - std::string senderUsername; - std::string recipient; - std::string subject; - std::string body; - uint64_t id{}; - uint32_t senderId{}; - uint32_t receiverId{}; - uint64_t timeSent{}; - bool wasRead{}; - struct { - LWOOBJID itemID{}; - int32_t itemCount{}; - LOT itemLOT{}; - LWOOBJID itemSubkey{}; - }; - }; - // Insert a new mail into the database. virtual void InsertNewMail(const MailInfo& mail) = 0; diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 081681411..dbaf23f83 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -79,14 +79,14 @@ class MySQLDatabase : public GameDatabase { void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; - void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; - std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; - std::optional GetMail(const uint64_t mailId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; uint32_t GetUnreadMailCount(const uint32_t characterId) override; void MarkMailRead(const uint64_t mailId) override; void DeleteMail(const uint64_t mailId) override; diff --git a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp index 63f5ceca3..74acb54f6 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp @@ -1,6 +1,7 @@ #include "MySQLDatabase.h" -void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { + +void MySQLDatabase::InsertNewMail(const MailInfo& mail) { ExecuteInsert( "INSERT INTO `mail` " "(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)" @@ -18,17 +19,17 @@ void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { mail.itemCount); } -std::vector MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { +std::vector MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { auto res = ExecuteSelect( "SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent" " FROM mail WHERE receiver_id=? limit ?;", characterId, numberOfMail); - std::vector toReturn; + std::vector toReturn; toReturn.reserve(res->rowsCount()); while (res->next()) { - IMail::MailInfo mail; + MailInfo mail; mail.id = res->getUInt64("id"); mail.subject = res->getString("subject").c_str(); mail.body = res->getString("body").c_str(); @@ -46,14 +47,14 @@ std::vector MySQLDatabase::GetMailForPlayer(const uint32_t char return toReturn; } -std::optional MySQLDatabase::GetMail(const uint64_t mailId) { +std::optional MySQLDatabase::GetMail(const uint64_t mailId) { auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId); if (!res->next()) { return std::nullopt; } - IMail::MailInfo toReturn; + MailInfo toReturn; toReturn.itemLOT = res->getInt("attachment_lot"); toReturn.itemCount = res->getInt("attachment_count"); diff --git a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h index a09c72c9f..8ef06eb51 100644 --- a/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h +++ b/dDatabase/GameDatabase/SQLite/SQLiteDatabase.h @@ -77,14 +77,14 @@ class SQLiteDatabase : public GameDatabase { void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; - void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; - std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; - std::optional GetMail(const uint64_t mailId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; uint32_t GetUnreadMailCount(const uint32_t characterId) override; void MarkMailRead(const uint64_t mailId) override; void DeleteMail(const uint64_t mailId) override; diff --git a/dDatabase/GameDatabase/SQLite/Tables/Mail.cpp b/dDatabase/GameDatabase/SQLite/Tables/Mail.cpp index 48c1e320d..edd4b672f 100644 --- a/dDatabase/GameDatabase/SQLite/Tables/Mail.cpp +++ b/dDatabase/GameDatabase/SQLite/Tables/Mail.cpp @@ -1,6 +1,6 @@ #include "SQLiteDatabase.h" -void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) { +void SQLiteDatabase::InsertNewMail(const MailInfo& mail) { ExecuteInsert( "INSERT INTO `mail` " "(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)" @@ -18,16 +18,16 @@ void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) { mail.itemCount); } -std::vector SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { +std::vector SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { auto [_, res] = ExecuteSelect( "SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent" " FROM mail WHERE receiver_id=? limit ?;", characterId, numberOfMail); - std::vector toReturn; + std::vector toReturn; while (!res.eof()) { - IMail::MailInfo mail; + MailInfo mail; mail.id = res.getInt64Field("id"); mail.subject = res.getStringField("subject"); mail.body = res.getStringField("body"); @@ -46,14 +46,14 @@ std::vector SQLiteDatabase::GetMailForPlayer(const uint32_t cha return toReturn; } -std::optional SQLiteDatabase::GetMail(const uint64_t mailId) { +std::optional SQLiteDatabase::GetMail(const uint64_t mailId) { auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId); if (res.eof()) { return std::nullopt; } - IMail::MailInfo toReturn; + MailInfo toReturn; toReturn.itemLOT = res.getIntField("attachment_lot"); toReturn.itemCount = res.getIntField("attachment_count"); diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp index 0263a6e39..20fd942eb 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.cpp @@ -184,7 +184,7 @@ void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& i } -void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { +void TestSQLDatabase::InsertNewMail(const MailInfo& mail) { } @@ -192,11 +192,11 @@ void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint3 } -std::vector TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { +std::vector TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { return {}; } -std::optional TestSQLDatabase::GetMail(const uint64_t mailId) { +std::optional TestSQLDatabase::GetMail(const uint64_t mailId) { return {}; } diff --git a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h index 9d4b184f0..70e870561 100644 --- a/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h +++ b/dDatabase/GameDatabase/TestSQL/TestSQLDatabase.h @@ -56,14 +56,14 @@ class TestSQLDatabase : public GameDatabase { void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; - void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewMail(const MailInfo& mail) override; void InsertNewUgcModel( std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) override; - std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; - std::optional GetMail(const uint64_t mailId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; uint32_t GetUnreadMailCount(const uint32_t characterId) override; void MarkMailRead(const uint64_t mailId) override; void DeleteMail(const uint64_t mailId) override; diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index d706af9c2..9f711068f 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -786,7 +786,7 @@ void CharacterComponent::AwardClaimCodes() { subject << "%[RewardCodes_" << rewardCode << "_subjectText]"; std::ostringstream body; body << "%[RewardCodes_" << rewardCode << "_bodyText]"; - Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1); + Mail::SendMail(m_Parent, subject.str(), body.str(), attachmentLOT, 1); } } diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index aa7614aaf..4e49abff4 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -274,7 +274,7 @@ void InventoryComponent::AddItem( switch (sourceType) { case 0: - Mail::SendMail(LWOOBJID_EMPTY, "Darkflame Universe", m_Parent, "Lost Reward", "You received an item and didn't have room for it.", lot, size); + Mail::SendMail(m_Parent, "%[MAIL_ACTIVITY_OVERFLOW_HEADER]", "%[MAIL_ACTIVITY_OVERFLOW_BODY]", lot, size); break; case 1: diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index f333851d0..c4c39e49a 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -26,12 +26,280 @@ #include "eMissionTaskType.h" #include "eReplicaComponentType.h" #include "eConnectionType.h" +#include "User.h" +#include "StringifiedEnum.h" + +namespace { + const std::string DefaultSender = "%[MAIL_SYSTEM_NOTIFICATION]"; +} + +namespace Mail { + std::map(const SystemAddress&, Entity* const)>> handlers = { + {eMessageID::SendRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + {eMessageID::DataRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + {eMessageID::AttachmentCollectRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + {eMessageID::DeleteRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + {eMessageID::ReadRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + {eMessageID::NotificationRequest, [](const SystemAddress& sysAddr, Entity* const player) { + return std::make_unique(); + }}, + }; + + void MailLUBitStream::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(messageID); + } + + bool MailLUBitStream::Deserialize(RakNet::BitStream& bitstream) { + VALIDATE_READ(bitstream.Read(messageID)); + return true; + } + + bool SendRequest::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(mailInfo.Deserialize(bitStream)); + return true; + } + + void SendRequest::Handle() { + //std::string subject = GeneralUtils::WStringToString(ReadFromPacket(packet, 50)); + //std::string body = GeneralUtils::WStringToString(ReadFromPacket(packet, 400)); + //std::string recipient = GeneralUtils::WStringToString(ReadFromPacket(packet, 32)); + + // Check if the player has restricted mail access + auto* character = player->GetCharacter(); + + if (!character) return; + + if (character->HasPermission(ePermissionMap::RestrictedMailAccess) || character->GetParentUser()->GetIsMuted()) { + // Send a message to the player + ChatPackets::SendSystemMessage( + sysAddr, + u"This character has restricted mail access." + ); + + SendResponse(Mail::eSendResponse::SenderAccountIsMuted).Send(sysAddr); + + return; + } + + //Cleanse recipient: + mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), ""); + + //Inventory::InventoryType itemType; + int mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee; + int stackSize = 0; + auto inv = static_cast(player->GetComponent(eReplicaComponentType::INVENTORY)); + Item* item = nullptr; + + if (mailInfo.itemID > 0 && mailInfo.itemCount > 0 && inv) { + item = inv->FindItemById(mailInfo.itemID); + if (item) { + mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee); + stackSize = item->GetCount(); + mailInfo.itemLOT = item->GetLot(); + } else { + SendResponse(eSendResponse::AttachmentNotFound).Send(sysAddr); + return; + } + } + + //Check if we can even send this mail (negative coins bug): + if (player->GetCharacter()->GetCoins() - mailCost < 0) { + SendResponse(eSendResponse::NotEnoughCoins).Send(sysAddr); + return; + } + + //Get the receiver's id: + auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient); + + if (!receiverID) { + SendResponse(Mail::eSendResponse::RecipientNotFound).Send(sysAddr); + return; + } + + //Check if we have a valid receiver: + if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) { + SendResponse(Mail::eSendResponse::CannotMailSelf).Send(sysAddr); + return; + } else { + Database::Get()->InsertNewMail(mailInfo); + } + + SendResponse(Mail::eSendResponse::Success).Send(sysAddr); + player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL); + + LOG("Seeing if we need to remove item with ID/count/LOT: %i %i %i", mailInfo.itemID, mailInfo.itemCount, mailInfo.itemLOT); + + if (inv && mailInfo.itemLOT != 0 && mailInfo.itemCount > 0 && item) { + LOG("Trying to remove item with ID/count/LOT: %i %i %i", mailInfo.itemID, mailInfo.itemCount, mailInfo.itemLOT); + inv->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, INVALID, true); + + auto* missionCompoent = player->GetComponent(); + + if (missionCompoent != nullptr) { + missionCompoent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount); + } + } + + character->SaveXMLToDatabase(); + } + + void SendResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(response); + } + + void NotificationResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(notification); + bitStream.Write(0); // unused + bitStream.Write(0); // unused + bitStream.Write(auctionID); + bitStream.Write(0); // unused + bitStream.Write(mailCount); + } + + void DataRequest::Handle() { + auto playerMail = Database::Get()->GetMailForPlayer(player->GetObjectID()); + + if (playerMail.size() > 0) { + DataResponse response; + response.playerMail = playerMail; + response.Send(sysAddr); + } + } + + void DataResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + + bitStream.Write(this->throttled); + bitStream.Write(this->playerMail.size()); + bitStream.Write(0); // packing + for (const auto& mail : this->playerMail) { + mail.Serialize(bitStream); + } + } + + bool AttachmentCollectRequest::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(mailID)); + VALIDATE_READ(bitStream.Read(playerID)); + return true; + } + + void AttachmentCollectRequest::Handle() { + if (mailID > 0 && playerID == player->GetObjectID()) { + auto playerMail = Database::Get()->GetMail(mailID); + + LOT attachmentLOT = 0; + uint32_t attachmentCount = 0; + + if (playerMail) { + attachmentLOT = playerMail->itemLOT; + attachmentCount = playerMail->itemCount; + } + + auto inv = player->GetComponent(); + if (!inv) return; + + inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL); + + Database::Get()->ClaimMailItem(mailID); + + AttachmentCollectResponse(eRemoveAttachmentResponse::Success, mailID).Send(sysAddr); + } + } + + void AttachmentCollectResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(mailID); + } + + bool DeleteRequest::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(mailID)); + VALIDATE_READ(bitStream.Read(playerID)); + return true; + } + + void DeleteRequest::Handle() { + DeleteResponse response(mailID); + if (mailID > 0) { + Database::Get()->DeleteMail(mailID); + response.status = eDeleteResponse::Success; + } + response.Send(sysAddr); + } + + void DeleteResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(mailID); + } + + bool ReadRequest::Deserialize(RakNet::BitStream& bitStream) { + int32_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); + VALIDATE_READ(bitStream.Read(mailID)); + return true; + } + + void ReadRequest::Handle() { + ReadResponse response; + response.mailID = mailID; + response.status = eReadResponse::Success; + + if (mailID > 0) Database::Get()->MarkMailRead(mailID); + else response.status = eReadResponse::UnknownError; + response.Send(sysAddr); + } + + void ReadResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(mailID); + } + + void NotificationRequest::Handle() { + auto unreadMailCount = Database::Get()->GetUnreadMailCount(player->GetObjectID()); + if (unreadMailCount > 0) NotificationResponse(eNotificationResponse::NewMail, unreadMailCount).Send(sysAddr); + } +} + +// Non Stuct Functions +void Mail::HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, Entity* player) { + MailLUBitStream data; + if (!data.Deserialize(inStream)) { + LOG_DEBUG("Error Reading Mail header"); + return; + } + + auto it = handlers.find(data.messageID); + if (it != handlers.end()) { + auto mail_data = it->second(sysAddr, player); + if (!mail_data->Deserialize(inStream)) { + LOG_DEBUG("Error Reading Mail Data"); + return; + } + mail_data->Handle(); + } else { + LOG_DEBUG("Unhandled Mail Packet with ID: %i", data.messageID); + } +} void Mail::SendMail(const Entity* recipient, const std::string& subject, const std::string& body, const LOT attachment, const uint16_t attachmentCount) { SendMail( LWOOBJID_EMPTY, - ServerName, + DefaultSender, recipient->GetObjectID(), recipient->GetCharacter()->GetName(), subject, @@ -46,7 +314,7 @@ void Mail::SendMail(const LWOOBJID recipient, const std::string& recipientName, const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) { SendMail( LWOOBJID_EMPTY, - ServerName, + DefaultSender, recipient, recipientName, subject, @@ -75,7 +343,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, const void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJID recipient, const std::string& recipientName, const std::string& subject, const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) { - IMail::MailInfo mailInsert; + MailInfo mailInsert; mailInsert.senderUsername = senderName; mailInsert.recipient = recipientName; mailInsert.subject = subject; @@ -91,254 +359,5 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server - SendNotification(sysAddr, 1); //Show the "one new mail" message -} - -void Mail::HandleMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) { - int mailStuffID = 0; - packet.Read(mailStuffID); - - auto returnVal = std::async(std::launch::async, [&packet, &sysAddr, entity, mailStuffID]() { - Mail::MailMessageID stuffID = MailMessageID(mailStuffID); - switch (stuffID) { - case MailMessageID::AttachmentCollect: - Mail::HandleAttachmentCollect(packet, sysAddr, entity); - break; - case MailMessageID::DataRequest: - Mail::HandleDataRequest(packet, sysAddr, entity); - break; - case MailMessageID::MailDelete: - Mail::HandleMailDelete(packet, sysAddr); - break; - case MailMessageID::MailRead: - Mail::HandleMailRead(packet, sysAddr); - break; - case MailMessageID::NotificationRequest: - Mail::HandleNotificationRequest(sysAddr, entity->GetObjectID()); - break; - case MailMessageID::Send: - Mail::HandleSendMail(packet, sysAddr, entity); - break; - default: - LOG("Unhandled and possibly undefined MailStuffID: %i", int(stuffID)); - } - }); -} - -void Mail::HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity) { - //std::string subject = GeneralUtils::WStringToString(ReadFromPacket(packet, 50)); - //std::string body = GeneralUtils::WStringToString(ReadFromPacket(packet, 400)); - //std::string recipient = GeneralUtils::WStringToString(ReadFromPacket(packet, 32)); - - // Check if the player has restricted mail access - auto* character = entity->GetCharacter(); - - if (!character) return; - - if (character->HasPermission(ePermissionMap::RestrictedMailAccess)) { - // Send a message to the player - ChatPackets::SendSystemMessage( - sysAddr, - u"This character has restricted mail access." - ); - - Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::SenderAccountIsMuted); - - return; - } - - LUWString subjectRead(50); - packet.Read(subjectRead); - - LUWString bodyRead(400); - packet.Read(bodyRead); - - LUWString recipientRead(32); - packet.Read(recipientRead); - - const std::string subject = subjectRead.GetAsString(); - const std::string body = bodyRead.GetAsString(); - - //Cleanse recipient: - const std::string recipient = std::regex_replace(recipientRead.GetAsString(), std::regex("[^0-9a-zA-Z]+"), ""); - - uint64_t unknown64 = 0; - LWOOBJID attachmentID; - uint16_t attachmentCount; - - packet.Read(unknown64); - packet.Read(attachmentID); - packet.Read(attachmentCount); //We don't care about the rest of the packet. - uint32_t itemID = static_cast(attachmentID); - LOT itemLOT = 0; - //Inventory::InventoryType itemType; - int mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee; - int stackSize = 0; - auto inv = static_cast(entity->GetComponent(eReplicaComponentType::INVENTORY)); - Item* item = nullptr; - - if (itemID > 0 && attachmentCount > 0 && inv) { - item = inv->FindItemById(attachmentID); - if (item) { - mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee); - stackSize = item->GetCount(); - itemLOT = item->GetLot(); - } else { - Mail::SendSendResponse(sysAddr, MailSendResponse::AttachmentNotFound); - return; - } - } - - //Check if we can even send this mail (negative coins bug): - if (entity->GetCharacter()->GetCoins() - mailCost < 0) { - Mail::SendSendResponse(sysAddr, MailSendResponse::NotEnoughCoins); - return; - } - - //Get the receiver's id: - auto receiverID = Database::Get()->GetCharacterInfo(recipient); - - if (!receiverID) { - Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::RecipientNotFound); - return; - } - - //Check if we have a valid receiver: - if (GeneralUtils::CaseInsensitiveStringCompare(recipient, character->GetName()) || receiverID->id == character->GetID()) { - Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::CannotMailSelf); - return; - } else { - IMail::MailInfo mailInsert; - mailInsert.senderUsername = character->GetName(); - mailInsert.recipient = recipient; - mailInsert.subject = subject; - mailInsert.body = body; - mailInsert.senderId = character->GetID(); - mailInsert.receiverId = receiverID->id; - mailInsert.itemCount = attachmentCount; - mailInsert.itemID = itemID; - mailInsert.itemLOT = itemLOT; - mailInsert.itemSubkey = LWOOBJID_EMPTY; - Database::Get()->InsertNewMail(mailInsert); - } - - Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success); - entity->GetCharacter()->SetCoins(entity->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL); - - LOG("Seeing if we need to remove item with ID/count/LOT: %i %i %i", itemID, attachmentCount, itemLOT); - - if (inv && itemLOT != 0 && attachmentCount > 0 && item) { - LOG("Trying to remove item with ID/count/LOT: %i %i %i", itemID, attachmentCount, itemLOT); - inv->RemoveItem(itemLOT, attachmentCount, INVALID, true); - - auto* missionCompoent = entity->GetComponent(); - - if (missionCompoent != nullptr) { - missionCompoent->Progress(eMissionTaskType::GATHER, itemLOT, LWOOBJID_EMPTY, "", -attachmentCount); - } - } - - character->SaveXMLToDatabase(); -} - -void Mail::HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player) { - auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20); - RakNet::BitStream bitStream; - Mail::Data data = Mail::Data(playerMail); - bitStream.Write(data); - Game::server->Send(bitStream, sysAddr, false); -} - -void Mail::HandleAttachmentCollect(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player) { - int unknown; - uint64_t mailID; - LWOOBJID playerID; - packet.Read(unknown); - packet.Read(mailID); - packet.Read(playerID); - - if (mailID > 0 && playerID == player->GetObjectID()) { - auto playerMail = Database::Get()->GetMail(mailID); - - LOT attachmentLOT = 0; - uint32_t attachmentCount = 0; - - if (playerMail) { - attachmentLOT = playerMail->itemLOT; - attachmentCount = playerMail->itemCount; - } - - auto inv = player->GetComponent(); - if (!inv) return; - - inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL); - - Mail::SendAttachmentRemoveConfirm(sysAddr, mailID); - - Database::Get()->ClaimMailItem(mailID); - } -} - -void Mail::HandleMailDelete(RakNet::BitStream& packet, const SystemAddress& sysAddr) { - int unknown; - uint64_t mailID; - LWOOBJID playerID; - packet.Read(unknown); - packet.Read(mailID); - packet.Read(playerID); - - if (mailID > 0) Mail::SendDeleteConfirm(sysAddr, mailID, playerID); -} - -void Mail::HandleMailRead(RakNet::BitStream& packet, const SystemAddress& sysAddr) { - int unknown; - uint64_t mailID; - packet.Read(unknown); - packet.Read(mailID); - - if (mailID > 0) Mail::SendReadConfirm(sysAddr, mailID); -} - -void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID) { - auto unreadMailCount = Database::Get()->GetUnreadMailCount(objectID); - - if (unreadMailCount > 0) Mail::SendNotification(sysAddr, unreadMailCount); -} - -void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) { - RakNet::BitStream bitStream; - Mail::Response data = Mail::Response(response); - bitStream.Write(data); - Game::server->Send(bitStream, sysAddr, false); -} - -void Mail::SendNotification(const SystemAddress& sysAddr, int mailCount) { - RakNet::BitStream bitStream; - Mail::Notification data = Mail::Notification(MailNotification::NewMail, mailCount); - Game::server->Send(bitStream, sysAddr, false); -} - -void Mail::SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID) { - RakNet::BitStream bitStream; - AttachmentCollect data = AttachmentCollect(MailRemoveAttachment::Success, mailID); - bitStream.Write(data); - Game::server->Send(bitStream, sysAddr, false); -} - -void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID) { - RakNet::BitStream bitStream; - DeleteMail data = DeleteMail(MailDeleteResponse::Success, mailID); - bitStream.Write(data); - Game::server->Send(bitStream, sysAddr, false); - - Database::Get()->DeleteMail(mailID); -} - -void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) { - RakNet::BitStream bitStream; - Read data = Read(MailReadResponse::Success, mailID); - bitStream.Write(data); - Game::server->Send(bitStream, sysAddr, false); - - Database::Get()->MarkMailRead(mailID); + NotificationResponse(eNotificationResponse::NewMail).Send(sysAddr); } diff --git a/dGame/dUtilities/Mail.h b/dGame/dUtilities/Mail.h index 6a78f47a0..42980ce4d 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -5,25 +5,24 @@ #include "BitStream.h" #include "RakNetTypes.h" #include "dCommonVars.h" - -template -struct LUHeader; +#include "BitStreamUtils.h" +#include "MailInfo.h" class Entity; namespace Mail { - enum class MailMessageID : uint32_t { - Send = 0, + enum class eMessageID : uint32_t { + SendRequest = 0, SendResponse, - Notification, + NotificationResponse, DataRequest, - MailData, - AttachmentCollect, - AttachmentCollectConfirm, - MailDelete, - MailDeleteConfirm, - MailRead, - MailReadConfirm, + DataResponse, + AttachmentCollectRequest, + AttachmentCollectResponse, + DeleteRequest, + DeleteResponse, + ReadRequest, + ReadResponse, NotificationRequest, AuctionCreate, AuctionCreationResponse, @@ -36,7 +35,7 @@ namespace Mail { UnknownError }; - enum class MailSendResponse : uint32_t { + enum class eSendResponse : uint32_t { Success = 0, NotEnoughCoins, AttachmentNotFound, @@ -54,15 +53,15 @@ namespace Mail { UnknownError }; - enum class MailDeleteResponse : uint32_t { + enum class eDeleteResponse : uint32_t { Success = 0, HasAttachements, - NotFoud, + NotFound, Throttled, UnknownError }; - enum class MailRemoveAttachment : uint32_t { + enum class eRemoveAttachmentResponse : uint32_t { Success = 0, AttachmentNotFound, NoSpaceInInventory, @@ -71,7 +70,7 @@ namespace Mail { UnknownError }; - enum class MailNotification : uint32_t { + enum class eNotificationResponse : uint32_t { NewMail = 0, UnHandled, AuctionWon, @@ -83,106 +82,118 @@ namespace Mail { UnknownError }; - enum class MailReadResponse : uint32_t { + enum class eReadResponse : uint32_t { Success = 0, UnknownError }; - struct MailInfo { - uint64_t id; - std::string senderUsername; - std::string recipient; - std::string subject; - std::string body; - uint32_t senderId; - uint32_t receiverId; - uint32_t itemCount; - uint32_t itemID; - LOT itemLOT; - LWOOBJID itemSubkey; + struct MailLUBitStream : public LUBitStream { + eMessageID messageID = eMessageID::UnknownError; + const SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + Entity* const player = nullptr; + + MailLUBitStream() = default; + MailLUBitStream(eMessageID _messageID) : LUBitStream(eConnectionType::CLIENT, MessageType::Client::MAIL), messageID{_messageID} {}; + MailLUBitStream(const SystemAddress& _sysAddr, Entity* const _player) : sysAddr(_sysAddr), player(_player) {}; + + virtual void Serialize(RakNet::BitStream& bitStream) const override; + virtual bool Deserialize(RakNet::BitStream& bitStream) override; + virtual void Handle() override {}; + }; + + struct SendRequest : public MailLUBitStream { + MailInfo mailInfo; + + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; + }; + + struct SendResponse :public MailLUBitStream { + eSendResponse response = eSendResponse::UnknownError; + + SendResponse(eSendResponse _response) : MailLUBitStream(eMessageID::SendResponse), response{_response} {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct NotificationResponse : public MailLUBitStream { + eNotificationResponse notification = eNotificationResponse::UnknownError; + LWOOBJID auctionID = LWOOBJID_EMPTY; + uint32_t mailCount = 1; + + NotificationResponse(eNotificationResponse _notification) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification} {}; + NotificationResponse(eNotificationResponse _notification, uint32_t _mailCount) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification}, mailCount{_mailCount} {}; + void Serialize(RakNet::BitStream& bitStream) const override; }; - struct MailLUHeader { - LUHeader header = LUHeader(eConnectionType::CLIENT, MessageType::Client::MAIL); - MailMessageID messageID; + struct DataRequest : public MailLUBitStream { + void Handle() override; }; - struct Data { - MailLUHeader header; + struct DataResponse : public MailLUBitStream { uint32_t throttled = 0; - std::vector playerMail; + std::vector playerMail; + + DataResponse() : MailLUBitStream(eMessageID::DataRequest) {}; + void Serialize(RakNet::BitStream& bitStream) const override; - Data() = delete; - Data(std::vector mailData) { - this->header.messageID = MailMessageID::MailData; - this->playerMail = mailData; - } }; - struct Response { - MailLUHeader header; - MailSendResponse response; + struct AttachmentCollectRequest : public MailLUBitStream { + uint64_t mailID = 0; + LWOOBJID playerID = LWOOBJID_EMPTY; - Response() = delete; - Response(MailSendResponse response) { - this->header.messageID = MailMessageID::SendResponse; - this->response = response; - } + AttachmentCollectRequest() : MailLUBitStream(eMessageID::AttachmentCollectRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; }; - struct Notification { - MailLUHeader header; - MailNotification notification; - LWOOBJID auctionID = 0; - uint32_t mailCount = 0; - - Notification() = delete; - Notification(MailNotification notification, uint32_t mailCount, LWOOBJID auctionID = LWOOBJID_EMPTY) { - this->header.messageID = MailMessageID::Notification; - this->notification = notification; - this->auctionID = auctionID; - this->mailCount = mailCount; - } + struct AttachmentCollectResponse : public MailLUBitStream { + eRemoveAttachmentResponse status = eRemoveAttachmentResponse::UnknownError; + uint64_t mailID = 0; + + AttachmentCollectResponse(eRemoveAttachmentResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status}, mailID{_mailID} {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct DeleteRequest : public MailLUBitStream { + uint64_t mailID = 0; + LWOOBJID playerID = LWOOBJID_EMPTY; + + + DeleteRequest() : MailLUBitStream(eMessageID::DeleteRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; }; - struct AttachmentCollect { - MailLUHeader header; - MailRemoveAttachment status; - uint64_t mailID; - - AttachmentCollect() = delete; - AttachmentCollect(MailRemoveAttachment status, uint64_t mailID) { - this->header.messageID = MailMessageID::AttachmentCollect; - this->status = status; - this->mailID = mailID; - } + struct DeleteResponse : public MailLUBitStream { + eDeleteResponse status = eDeleteResponse::UnknownError; + uint64_t mailID = 0; + DeleteResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::DeleteResponse), mailID{_mailID} {}; + void Serialize(RakNet::BitStream& bitStream) const override; }; - struct DeleteMail { - MailLUHeader header; - MailDeleteResponse status; - uint64_t mailID; + struct ReadRequest : public MailLUBitStream { + uint64_t mailID = 0; - DeleteMail() = delete; - DeleteMail(MailDeleteResponse status, uint64_t mailID) { - this->status = status; - this->mailID = mailID; - } + ReadRequest() : MailLUBitStream(eMessageID::ReadRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; }; - struct Read { - MailLUHeader header; - MailReadResponse status; - uint64_t mailID; + struct ReadResponse : public MailLUBitStream { + uint64_t mailID = 0; + eReadResponse status = eReadResponse::UnknownError; + + ReadResponse() : MailLUBitStream(eMessageID::ReadResponse) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; - Read() = delete; - Read(MailReadResponse status, uint64_t mailID) { - this->status = status; - this->mailID = mailID; - } + struct NotificationRequest : public MailLUBitStream { + NotificationRequest() : MailLUBitStream(eMessageID::NotificationRequest) {}; + void Handle() override; }; - const std::string ServerName = "Darkflame Universe"; + void HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, Entity* player); void SendMail( const Entity* recipient, @@ -223,113 +234,6 @@ namespace Mail { uint16_t attachmentCount, const SystemAddress& sysAddr ); - - void HandleMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity); - void HandleSendMail(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* entity); - void HandleDataRequest(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player); - void HandleAttachmentCollect(RakNet::BitStream& packet, const SystemAddress& sysAddr, Entity* player); - void HandleMailDelete(RakNet::BitStream& packet, const SystemAddress& sysAddr); - void HandleMailRead(RakNet::BitStream& packet, const SystemAddress& sysAddr); - void HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID); - - void SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response); - void SendNotification(const SystemAddress& sysAddr, int mailCount); - void SendAttachmentRemoveConfirm(const SystemAddress& sysAddr, uint64_t mailID); - void SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOOBJID playerID); - void SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID); }; - -namespace RakNet { - template <> - inline void RakNet::BitStream::Write(Mail::MailLUHeader data) { - this->Write>(data.header); - this->Write(static_cast(data.messageID)); - } - - template <> - inline void RakNet::BitStream::Write(Mail::Data data) { - - this->Write(data.header); - this->Write(data.throttled); - - this->Write(data.playerMail.size()); - this->Write(0); // packing - for (auto& mail : data.playerMail) { - this->Write(mail.id); - - const LUWString subject(mail.subject, 50); - this->Write(subject); - - const LUWString body(mail.body, 400); - this->Write(body); - - const LUWString sender(mail.senderUsername, 32); - this->Write(sender); - this->Write(uint32_t(0)); // packing - - this->Write(uint64_t(0)); // attachedCurrency - this->Write(mail.itemID); - - LOT lot = mail.itemLOT; - if (lot <= 0) this->Write(LOT(-1)); - else this->Write(lot); - this->Write(uint32_t(0)); // packing - - this->Write(mail.itemSubkey); - - this->Write(mail.itemCount); - this->Write(uint8_t(0)); // subject type (used for auction) - this->Write(uint8_t(0)); // packing - this->Write(uint32_t(0)); // packing - - this->Write(mail.timeSent); // expiration date - this->Write(mail.timeSent);// send date - this->Write(mail.wasRead); // was read - - this->Write(uint8_t(0)); // isLocalized - this->Write(uint16_t(0)); // packing - this->Write(uint32_t(0)); // packing - } - } - - template <> - inline void RakNet::BitStream::Write(Mail::Response data) { - this->Write(data.header); - this->Write(static_cast(data.response)); - } - - template <> - inline void RakNet::BitStream::Write(Mail::Notification data) { - this->Write(data.header); - this->Write(data.notification); - this->Write(0); // unused - this->Write(0); // unused - this->Write(data.auctionID); - this->Write(0); // unused - this->Write(data.mailCount); - } - - template <> - inline void RakNet::BitStream::Write(Mail::AttachmentCollect data) { - this->Write(data.header); - this->Write(data.status); - this->Write(data.mailID); - } - - template <> - inline void RakNet::BitStream::Write(Mail::DeleteMail data) { - this->Write(data.header); - this->Write(data.status); - this->Write(data.mailID); - } - - template <> - inline void RakNet::BitStream::Write(Mail::Read data) { - this->Write(data.header); - this->Write(data.status); - this->Write(data.mailID); - } -} - #endif // !__MAIL_H__ diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index 3032a33bc..5c710c09e 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -102,7 +102,7 @@ namespace GMGreaterThanZeroCommands { return; } - IMail::MailInfo mailInsert; + MailInfo mailInsert; mailInsert.senderId = entity->GetObjectID(); mailInsert.senderUsername = "Darkflame Universe"; mailInsert.receiverId = receiverID; diff --git a/dNet/BitStreamUtils.cpp b/dNet/BitStreamUtils.cpp new file mode 100644 index 000000000..64142e990 --- /dev/null +++ b/dNet/BitStreamUtils.cpp @@ -0,0 +1,29 @@ +#include "BitStreamUtils.h" +#include "dServer.h" +#include "BitStream.h" + + +void LUBitStream::WriteHeader(RakNet::BitStream& bitStream) const { + bitStream.Write(ID_USER_PACKET_ENUM); + bitStream.Write(this->connectionType); + bitStream.Write(this->internalPacketID); + bitStream.Write(0); // padding +} + +bool LUBitStream::ReadHeader(RakNet::BitStream& bitStream) { + MessageID messageID; + bitStream.Read(messageID); + if (messageID != ID_USER_PACKET_ENUM) return false; + VALIDATE_READ(bitStream.Read(this->connectionType)); + VALIDATE_READ(bitStream.Read(this->internalPacketID)); + uint8_t padding; + VALIDATE_READ(bitStream.Read(padding)); + return true; +} + +void LUBitStream::Send(const SystemAddress& sysAddr) const { + RakNet::BitStream bitStream; + this->WriteHeader(bitStream); + this->Serialize(bitStream); + Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); +} diff --git a/dNet/BitStreamUtils.h b/dNet/BitStreamUtils.h index d55df14ad..d9e9bc9b1 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -3,18 +3,12 @@ #include "GeneralUtils.h" #include "BitStream.h" +#include "MessageIdentifiers.h" +#include "eConnectionType.h" #include #include -namespace MessageType { - enum class Auth : uint32_t; - enum class Client : uint32_t; - enum class Server : uint32_t; - enum class World : uint32_t; - enum class Master : uint32_t; -} - -enum class eConnectionType : uint16_t; +#define VALIDATE_READ(x) do { if (!x) return false; } while (0) struct LUString { std::string string; @@ -52,19 +46,28 @@ struct LUWString { }; }; -template -struct LUHeader { - MessageID messageID; - eConnectionType connectionType; - T internalPacketID; +struct LUBitStream { + eConnectionType connectionType = eConnectionType::UNKNOWN; + uint32_t internalPacketID = 0xFFFFFFFF; + + LUBitStream() = default; - LUHeader(eConnectionType connectionType, T internalPacketID, MessageID messageID = ID_USER_PACKET_ENUM) { - this->messageID = messageID; + template + LUBitStream(eConnectionType connectionType, T internalPacketID) { this->connectionType = connectionType; - this->internalPacketID = internalPacketID; - }; + this->internalPacketID = static_cast(internalPacketID); + } + + void WriteHeader(RakNet::BitStream& bitStream) const; + bool ReadHeader(RakNet::BitStream& bitStream); + void Send(const SystemAddress& sysAddr) const; + + virtual void Serialize(RakNet::BitStream& bitStream) const {} + virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; } + virtual void Handle() {}; }; + namespace BitStreamUtils { template void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) { @@ -119,46 +122,6 @@ namespace RakNet { value.string.resize(value.size); this->Write(value.string); } - - template <> - inline void RakNet::BitStream::Write>(LUHeader value) { - this->Write(value.messageID); - this->Write(value.connectionType); - this->Write(static_cast(value.internalPacketID)); - this->Write(0); - } - - template <> - inline void RakNet::BitStream::Write>(LUHeader value) { - this->Write(value.messageID); - this->Write(value.connectionType); - this->Write(static_cast(value.internalPacketID)); - this->Write(0); - } - - template <> - inline void RakNet::BitStream::Write>(LUHeader value) { - this->Write(value.messageID); - this->Write(value.connectionType); - this->Write(static_cast(value.internalPacketID)); - this->Write(0); - } - - template <> - inline void RakNet::BitStream::Write>(LUHeader value) { - this->Write(value.messageID); - this->Write(value.connectionType); - this->Write(static_cast(value.internalPacketID)); - this->Write(0); - } - - template <> - inline void RakNet::BitStream::Write>(LUHeader value) { - this->Write(value.messageID); - this->Write(value.connectionType); - this->Write(static_cast(value.internalPacketID)); - this->Write(0); - } }; #endif //!__BITSTREAMUTILS__H__ diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index 172aee205..84ba7e3ff 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -1,4 +1,5 @@ set(DNET_SOURCES "AuthPackets.cpp" + "BitStreamUtils.cpp" "ChatPackets.cpp" "ClientPackets.cpp" "dServer.cpp" diff --git a/dNet/MailInfo.cpp b/dNet/MailInfo.cpp new file mode 100644 index 000000000..687fee2e6 --- /dev/null +++ b/dNet/MailInfo.cpp @@ -0,0 +1,87 @@ +#include "MailInfo.h" +#include "BitStream.h" +#include "DluAssert.h" + +void MailInfo::Serialize(RakNet::BitStream& bitStream) const { + bitStream.Write(id); + + const LUWString subject(this->subject, 50); + bitStream.Write(subject); + + const LUWString body(this->body, 400); + bitStream.Write(body); + + const LUWString sender(this->senderUsername, 32); + bitStream.Write(sender); + bitStream.Write(0); // packing + + bitStream.Write(0); // attachedCurrency + bitStream.Write(itemID); + + LOT lot = itemLOT; + if (lot <= 0) bitStream.Write(LOT_NULL); + else bitStream.Write(lot); + bitStream.Write(0); // packing + + bitStream.Write(itemSubkey); + + bitStream.Write(itemCount); + bitStream.Write(0); // subject type (used for auction) + bitStream.Write(0); // packing + bitStream.Write(0); // packing + + bitStream.Write(timeSent); // expiration date + bitStream.Write(timeSent);// send date + bitStream.Write(wasRead); // was read + + bitStream.Write(0); // isLocalized + bitStream.Write(0); // packing + bitStream.Write(0); // packing +} + +bool MailInfo::Deserialize(RakNet::BitStream& bitStream) { + VALIDATE_READ(bitStream.Read(id)); + + LUWString subject(50); + VALIDATE_READ(bitStream.Read(subject)); + this->subject = subject.GetAsString(); + + LUWString body(400); + VALIDATE_READ(bitStream.Read(body)); + this->body = body.GetAsString(); + + LUWString sender(32); + VALIDATE_READ(bitStream.Read(sender)); + this->senderUsername = sender.GetAsString(); + + bitStream.IgnoreBytes(4); // packing + + bitStream.IgnoreBytes(8); // attachedCurrency + VALIDATE_READ(bitStream.Read(itemID)); + + LOT lot; + VALIDATE_READ(bitStream.Read(lot)); + if (lot == LOT_NULL) itemLOT = 0; + else itemLOT = lot; + bitStream.IgnoreBytes(4); // packing + + VALIDATE_READ(bitStream.Read(itemSubkey)); + + VALIDATE_READ(bitStream.Read(itemCount)); + + bitStream.IgnoreBytes(1); // subject type (used for auction) + bitStream.IgnoreBytes(1); // packing + bitStream.IgnoreBytes(4); // packing + + VALIDATE_READ(bitStream.Read(timeSent)); // expiration date + VALIDATE_READ(bitStream.Read(timeSent)); // send date + VALIDATE_READ(bitStream.Read(wasRead)); // was read + + bitStream.IgnoreBytes(1); // isLocalized + bitStream.IgnoreBytes(2); // packing + bitStream.IgnoreBytes(4); // packing + + DluAssert(bitStream.GetNumberOfUnreadBits() == 0); + + return true; +} \ No newline at end of file diff --git a/dNet/MailInfo.h b/dNet/MailInfo.h new file mode 100644 index 000000000..75100d36f --- /dev/null +++ b/dNet/MailInfo.h @@ -0,0 +1,33 @@ +#ifndef __MAILINFO_H__ +#define __MAILINFO_H__ + +#include +#include +#include "dCommonVars.h" + +namespace RakNet { + class BitStream; +} + +struct MailInfo { + std::string senderUsername; + std::string recipient; + std::string subject; + std::string body; + uint64_t id{}; + uint32_t senderId{}; + uint32_t receiverId{}; + uint64_t timeSent{}; + bool wasRead{}; + struct { + LWOOBJID itemID{}; + int32_t itemCount{}; + LOT itemLOT{}; + LWOOBJID itemSubkey{}; + }; + + void Serialize(RakNet::BitStream& bitStream) const {} + bool Deserialize(RakNet::BitStream& bitStream) { return true; } +}; + +#endif // __MAILINFO_H__ diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index a467ac0f1..44c1a5fcf 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -831,15 +831,20 @@ void HandlePacket(Packet* packet) { } if (packet->data[0] != ID_USER_PACKET_ENUM || packet->length < 4) return; - if (static_cast(packet->data[1]) == eConnectionType::SERVER) { - if (static_cast(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) { + + CINSTREAM; + LUBitStream luBitStream; + luBitStream.ReadHeader(inStream); + + if (luBitStream.connectionType == eConnectionType::SERVER) { + if (static_cast(luBitStream.internalPacketID) == MessageType::Server::VERSION_CONFIRM) { AuthPackets::HandleHandshake(Game::server, packet); } } - if (static_cast(packet->data[1]) != eConnectionType::WORLD) return; + if (luBitStream.connectionType != eConnectionType::WORLD) return; - switch (static_cast(packet->data[3])) { + switch (static_cast(luBitStream.internalPacketID)) { case MessageType::World::VALIDATION: { CINSTREAM_SKIP_HEADER; LUWString username; @@ -1185,11 +1190,7 @@ void HandlePacket(Packet* packet) { } case MessageType::World::MAIL: { - RakNet::BitStream bitStream(packet->data, packet->length, false); - // FIXME: Change this to the macro to skip the header... - LWOOBJID space; - bitStream.Read(space); - Mail::HandleMail(bitStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); + Mail::HandleMail(inStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); break; } From b7c579fb84a7d60b4f9c0843d9fa57522332ce99 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sun, 19 Jan 2025 16:31:54 -0600 Subject: [PATCH 3/9] make it compile and cleanup --- dGame/dComponents/MissionComponent.cpp | 9 --------- dGame/dUtilities/Mail.cpp | 12 ++++++------ dGame/dUtilities/SlashCommands/GMZeroCommands.cpp | 3 ++- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/dGame/dComponents/MissionComponent.cpp b/dGame/dComponents/MissionComponent.cpp index 0760d8e40..ea03d0e01 100644 --- a/dGame/dComponents/MissionComponent.cpp +++ b/dGame/dComponents/MissionComponent.cpp @@ -99,17 +99,8 @@ void MissionComponent::AcceptMission(const uint32_t missionId, const bool skipCh mission->Accept(); this->m_Missions.insert_or_assign(missionId, mission); - - if (missionId == 1728) { - //Needs to send a mail - - auto address = m_Parent->GetSystemAddress(); - - Mail::HandleNotificationRequest(address, m_Parent->GetObjectID()); - } } - void MissionComponent::CompleteMission(const uint32_t missionId, const bool skipChecks, const bool yieldRewards) { // Get the mission first auto* mission = this->GetMission(missionId); diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index c4c39e49a..db6609163 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -169,7 +169,7 @@ namespace Mail { } void DataRequest::Handle() { - auto playerMail = Database::Get()->GetMailForPlayer(player->GetObjectID()); + auto playerMail = Database::Get()->GetMailForPlayer(player->GetObjectID(), 20); if (playerMail.size() > 0) { DataResponse response; @@ -284,14 +284,14 @@ void Mail::HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, auto it = handlers.find(data.messageID); if (it != handlers.end()) { - auto mail_data = it->second(sysAddr, player); - if (!mail_data->Deserialize(inStream)) { - LOG_DEBUG("Error Reading Mail Data"); + auto request = it->second(sysAddr, player); + if (!request->Deserialize(inStream)) { + LOG_DEBUG("Error Reading Mail Request: %s", StringifiedEnum::ToString(data.messageID).data()); return; } - mail_data->Handle(); + request->Handle(); } else { - LOG_DEBUG("Unhandled Mail Packet with ID: %i", data.messageID); + LOG_DEBUG("Unhandled Mail Request with ID: %i", data.messageID); } } diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 51fa6e15b..092676941 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -12,6 +12,7 @@ #include "VanityUtilities.h" #include "WorldPackets.h" #include "ZoneInstanceManager.h" +#include "Database.h" // Components #include "BuffComponent.h" @@ -216,7 +217,7 @@ namespace GMZeroCommands { } void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID()); + Mail::NotificationResponse(Mail::eNotificationResponse::NewMail, Database::Get()->GetUnreadMailCount(entity->GetObjectID())).Send(sysAddr); } void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { From b01b3cc38d0caba6a83b20612dc676a21bf8f212 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sun, 19 Jan 2025 19:07:55 -0600 Subject: [PATCH 4/9] WIP debugging --- dDatabase/GameDatabase/MySQL/Tables/Mail.cpp | 1 + dGame/dUtilities/Mail.cpp | 51 ++++++++++++------- dGame/dUtilities/Mail.h | 8 +-- .../SlashCommands/GMZeroCommands.cpp | 2 +- dMasterServer/MasterServer.cpp | 2 +- dNet/BitStreamUtils.cpp | 3 ++ dNet/CMakeLists.txt | 1 + dNet/MailInfo.cpp | 10 ++-- dNet/MailInfo.h | 4 +- 9 files changed, 52 insertions(+), 30 deletions(-) diff --git a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp index 74acb54f6..51355080f 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp @@ -62,6 +62,7 @@ std::optional MySQLDatabase::GetMail(const uint64_t mailId) { } uint32_t MySQLDatabase::GetUnreadMailCount(const uint32_t characterId) { + LOG("Getting unread mail count for character %i", characterId); auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId); if (!res->next()) { diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index db6609163..e1c7dbd00 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -34,28 +34,29 @@ namespace { } namespace Mail { - std::map(const SystemAddress&, Entity* const)>> handlers = { - {eMessageID::SendRequest, [](const SystemAddress& sysAddr, Entity* const player) { + std::map()>> g_Handlers = { + {eMessageID::SendRequest, []() { return std::make_unique(); }}, - {eMessageID::DataRequest, [](const SystemAddress& sysAddr, Entity* const player) { + {eMessageID::DataRequest, []() { return std::make_unique(); }}, - {eMessageID::AttachmentCollectRequest, [](const SystemAddress& sysAddr, Entity* const player) { + {eMessageID::AttachmentCollectRequest, []() { return std::make_unique(); }}, - {eMessageID::DeleteRequest, [](const SystemAddress& sysAddr, Entity* const player) { + {eMessageID::DeleteRequest, []() { return std::make_unique(); }}, - {eMessageID::ReadRequest, [](const SystemAddress& sysAddr, Entity* const player) { + {eMessageID::ReadRequest, []() { return std::make_unique(); }}, - {eMessageID::NotificationRequest, [](const SystemAddress& sysAddr, Entity* const player) { + {eMessageID::NotificationRequest, []() { return std::make_unique(); }}, }; void MailLUBitStream::Serialize(RakNet::BitStream& bitStream) const { + LOG("Writing %s", StringifiedEnum::ToString(messageID).data()); bitStream.Write(messageID); } @@ -160,28 +161,32 @@ namespace Mail { void NotificationResponse::Serialize(RakNet::BitStream& bitStream) const { MailLUBitStream::Serialize(bitStream); + LOG("notification: %s", StringifiedEnum::ToString(notification).data()); bitStream.Write(notification); bitStream.Write(0); // unused bitStream.Write(0); // unused + LOG("auctionID: %llu", auctionID); bitStream.Write(auctionID); bitStream.Write(0); // unused + LOG("mailCount: %i", mailCount); bitStream.Write(mailCount); } void DataRequest::Handle() { - auto playerMail = Database::Get()->GetMailForPlayer(player->GetObjectID(), 20); - - if (playerMail.size() > 0) { - DataResponse response; - response.playerMail = playerMail; - response.Send(sysAddr); - } + LOG("DataRequest::Handle()"); + auto playerMail = Database::Get()->GetMailForPlayer(static_cast(player->GetObjectID()), 20); + LOG("DataRequest::Handle() - Got %i mail", playerMail.size()); + DataResponse response; + response.playerMail = playerMail; + response.Send(sysAddr); } void DataResponse::Serialize(RakNet::BitStream& bitStream) const { MailLUBitStream::Serialize(bitStream); - + LOG("throtttled: %i", throttled); bitStream.Write(this->throttled); + + LOG("playerMail.size(): %i", this->playerMail.size()); bitStream.Write(this->playerMail.size()); bitStream.Write(0); // packing for (const auto& mail : this->playerMail) { @@ -269,7 +274,13 @@ namespace Mail { } void NotificationRequest::Handle() { - auto unreadMailCount = Database::Get()->GetUnreadMailCount(player->GetObjectID()); + auto character = player->GetCharacter(); + if (!character) { + NotificationResponse(eNotificationResponse::UnknownError, 0).Send(sysAddr); + return; + } + + auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID()); if (unreadMailCount > 0) NotificationResponse(eNotificationResponse::NewMail, unreadMailCount).Send(sysAddr); } } @@ -282,9 +293,11 @@ void Mail::HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, return; } - auto it = handlers.find(data.messageID); - if (it != handlers.end()) { - auto request = it->second(sysAddr, player); + auto it = g_Handlers.find(data.messageID); + if (it != g_Handlers.end()) { + auto request = it->second(); + request->sysAddr = sysAddr; + request->player = player; if (!request->Deserialize(inStream)) { LOG_DEBUG("Error Reading Mail Request: %s", StringifiedEnum::ToString(data.messageID).data()); return; diff --git a/dGame/dUtilities/Mail.h b/dGame/dUtilities/Mail.h index 42980ce4d..ab60c2019 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -89,12 +89,11 @@ namespace Mail { struct MailLUBitStream : public LUBitStream { eMessageID messageID = eMessageID::UnknownError; - const SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS; - Entity* const player = nullptr; + SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + Entity* player = nullptr; MailLUBitStream() = default; MailLUBitStream(eMessageID _messageID) : LUBitStream(eConnectionType::CLIENT, MessageType::Client::MAIL), messageID{_messageID} {}; - MailLUBitStream(const SystemAddress& _sysAddr, Entity* const _player) : sysAddr(_sysAddr), player(_player) {}; virtual void Serialize(RakNet::BitStream& bitStream) const override; virtual bool Deserialize(RakNet::BitStream& bitStream) override; @@ -119,13 +118,13 @@ namespace Mail { eNotificationResponse notification = eNotificationResponse::UnknownError; LWOOBJID auctionID = LWOOBJID_EMPTY; uint32_t mailCount = 1; - NotificationResponse(eNotificationResponse _notification) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification} {}; NotificationResponse(eNotificationResponse _notification, uint32_t _mailCount) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification}, mailCount{_mailCount} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; struct DataRequest : public MailLUBitStream { + bool Deserialize(RakNet::BitStream& bitStream) override { return true; }; void Handle() override; }; @@ -190,6 +189,7 @@ namespace Mail { struct NotificationRequest : public MailLUBitStream { NotificationRequest() : MailLUBitStream(eMessageID::NotificationRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override { return true; }; void Handle() override; }; diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 092676941..6b9e62019 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -217,7 +217,7 @@ namespace GMZeroCommands { } void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - Mail::NotificationResponse(Mail::eNotificationResponse::NewMail, Database::Get()->GetUnreadMailCount(entity->GetObjectID())).Send(sysAddr); + Mail::NotificationResponse(Mail::eNotificationResponse::NewMail, Database::Get()->GetUnreadMailCount(entity->GetCharacter()->GetID())).Send(sysAddr); } void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 89ecef058..fcac8143a 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -348,7 +348,7 @@ int main(int argc, char** argv) { StartChatServer(); Game::im->GetInstance(0, false, 0); - Game::im->GetInstance(1000, false, 0); + Game::im->GetInstance(1100, false, 0); StartAuthServer(); } diff --git a/dNet/BitStreamUtils.cpp b/dNet/BitStreamUtils.cpp index 64142e990..8fc5d20dc 100644 --- a/dNet/BitStreamUtils.cpp +++ b/dNet/BitStreamUtils.cpp @@ -1,6 +1,7 @@ #include "BitStreamUtils.h" #include "dServer.h" #include "BitStream.h" +#include "PacketUtils.h" void LUBitStream::WriteHeader(RakNet::BitStream& bitStream) const { @@ -25,5 +26,7 @@ void LUBitStream::Send(const SystemAddress& sysAddr) const { RakNet::BitStream bitStream; this->WriteHeader(bitStream); this->Serialize(bitStream); + LOG("%s", sysAddr.ToString(true)); + PacketUtils::SavePacket("mailv2", reinterpret_cast(bitStream.GetData()), bitStream.GetNumberOfBytesUsed()); Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index 84ba7e3ff..67c2ea1fa 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -3,6 +3,7 @@ set(DNET_SOURCES "AuthPackets.cpp" "ChatPackets.cpp" "ClientPackets.cpp" "dServer.cpp" + "MailInfo.cpp" "MasterPackets.cpp" "PacketUtils.cpp" "WorldPackets.cpp" diff --git a/dNet/MailInfo.cpp b/dNet/MailInfo.cpp index 687fee2e6..6ca1de474 100644 --- a/dNet/MailInfo.cpp +++ b/dNet/MailInfo.cpp @@ -3,22 +3,26 @@ #include "DluAssert.h" void MailInfo::Serialize(RakNet::BitStream& bitStream) const { + LOG("Writing MailInfo"); + LOG("ID: %llu", id); bitStream.Write(id); - + LOG("Subject: %s", subject.c_str()); const LUWString subject(this->subject, 50); bitStream.Write(subject); - + LOG("Body: %s", body.c_str()); const LUWString body(this->body, 400); bitStream.Write(body); - + LOG("Sender: %s", senderUsername.c_str()); const LUWString sender(this->senderUsername, 32); bitStream.Write(sender); bitStream.Write(0); // packing bitStream.Write(0); // attachedCurrency + LOG("ItemID: %llu", itemID); bitStream.Write(itemID); LOT lot = itemLOT; + LOG("ItemLOT: %u", lot); if (lot <= 0) bitStream.Write(LOT_NULL); else bitStream.Write(lot); bitStream.Write(0); // packing diff --git a/dNet/MailInfo.h b/dNet/MailInfo.h index 75100d36f..2159c0719 100644 --- a/dNet/MailInfo.h +++ b/dNet/MailInfo.h @@ -26,8 +26,8 @@ struct MailInfo { LWOOBJID itemSubkey{}; }; - void Serialize(RakNet::BitStream& bitStream) const {} - bool Deserialize(RakNet::BitStream& bitStream) { return true; } + void Serialize(RakNet::BitStream& bitStream) const; + bool Deserialize(RakNet::BitStream& bitStream); }; #endif // __MAILINFO_H__ From a07d54e513bad6e5a2ad90ceea6f3f8edb5904d8 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 20 Jan 2025 00:42:28 -0600 Subject: [PATCH 5/9] all tested and working --- dDatabase/GameDatabase/MySQL/Tables/Mail.cpp | 1 - dGame/dUtilities/Mail.cpp | 213 +++++++++---------- dGame/dUtilities/Mail.h | 40 +++- dGame/dUtilities/SlashCommandHandler.cpp | 2 +- dMasterServer/MasterServer.cpp | 2 +- dNet/BitStreamUtils.cpp | 2 - dNet/MailInfo.cpp | 46 +--- dNet/MailInfo.h | 3 +- 8 files changed, 139 insertions(+), 170 deletions(-) diff --git a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp index 51355080f..74acb54f6 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp @@ -62,7 +62,6 @@ std::optional MySQLDatabase::GetMail(const uint64_t mailId) { } uint32_t MySQLDatabase::GetUnreadMailCount(const uint32_t characterId) { - LOG("Getting unread mail count for character %i", characterId); auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId); if (!res->next()) { diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index e1c7dbd00..836628c6a 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -56,7 +56,6 @@ namespace Mail { }; void MailLUBitStream::Serialize(RakNet::BitStream& bitStream) const { - LOG("Writing %s", StringifiedEnum::ToString(messageID).data()); bitStream.Write(messageID); } @@ -71,111 +70,89 @@ namespace Mail { } void SendRequest::Handle() { - //std::string subject = GeneralUtils::WStringToString(ReadFromPacket(packet, 50)); - //std::string body = GeneralUtils::WStringToString(ReadFromPacket(packet, 400)); - //std::string recipient = GeneralUtils::WStringToString(ReadFromPacket(packet, 32)); - - // Check if the player has restricted mail access + SendResponse response(eSendResponse::UnknownError); auto* character = player->GetCharacter(); - - if (!character) return; - - if (character->HasPermission(ePermissionMap::RestrictedMailAccess) || character->GetParentUser()->GetIsMuted()) { - // Send a message to the player - ChatPackets::SendSystemMessage( - sysAddr, - u"This character has restricted mail access." - ); - - SendResponse(Mail::eSendResponse::SenderAccountIsMuted).Send(sysAddr); - - return; - } - - //Cleanse recipient: - mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), ""); - - //Inventory::InventoryType itemType; - int mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee; - int stackSize = 0; - auto inv = static_cast(player->GetComponent(eReplicaComponentType::INVENTORY)); - Item* item = nullptr; - - if (mailInfo.itemID > 0 && mailInfo.itemCount > 0 && inv) { - item = inv->FindItemById(mailInfo.itemID); - if (item) { - mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee); - stackSize = item->GetCount(); - mailInfo.itemLOT = item->GetLot(); + if (character && !(character->HasPermission(ePermissionMap::RestrictedMailAccess) || character->GetParentUser()->GetIsMuted())) { + mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), ""); + auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient); + + if (!receiverID) { + response.status = eSendResponse::RecipientNotFound; + } else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) { + response.status = eSendResponse::CannotMailSelf; } else { - SendResponse(eSendResponse::AttachmentNotFound).Send(sysAddr); - return; + uint32_t mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee; + uint32_t stackSize = 0; + + auto inventoryComponent = player->GetComponent(); + Item* item = nullptr; + + bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0; + + if (hasAttachment) { + item = inventoryComponent->FindItemById(mailInfo.itemID); + if (item) { + mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig()->mailPercentAttachmentFee); + mailInfo.itemLOT = item->GetLot(); + } + } + + if (hasAttachment && !item) { + response.status = eSendResponse::AttachmentNotFound; + } else if (player->GetCharacter()->GetCoins() - mailCost < 0) { + response.status = eSendResponse::NotEnoughCoins; + } else { + bool removeSuccess = true; + // Remove coins and items from the sender + player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL); + if (inventoryComponent && hasAttachment && item) { + removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, INVALID, true); + auto* missionComponent = player->GetComponent(); + if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount); + } + + // we passed all the checks, now we can actully send the mail + if (removeSuccess) { + mailInfo.senderId = character->GetID(); + mailInfo.senderUsername = character->GetName(); + mailInfo.receiverId = receiverID->id; + mailInfo.itemSubkey = LWOOBJID_EMPTY; + + //clear out the attachementID + mailInfo.itemID = 0; + + Database::Get()->InsertNewMail(mailInfo); + response.status = eSendResponse::Success; + character->SaveXMLToDatabase(); + } else { + response.status = eSendResponse::AttachmentNotFound; + } + } } - } - - //Check if we can even send this mail (negative coins bug): - if (player->GetCharacter()->GetCoins() - mailCost < 0) { - SendResponse(eSendResponse::NotEnoughCoins).Send(sysAddr); - return; - } - - //Get the receiver's id: - auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient); - - if (!receiverID) { - SendResponse(Mail::eSendResponse::RecipientNotFound).Send(sysAddr); - return; - } - - //Check if we have a valid receiver: - if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) { - SendResponse(Mail::eSendResponse::CannotMailSelf).Send(sysAddr); - return; } else { - Database::Get()->InsertNewMail(mailInfo); + response.status = eSendResponse::SenderAccountIsMuted; } - - SendResponse(Mail::eSendResponse::Success).Send(sysAddr); - player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL); - - LOG("Seeing if we need to remove item with ID/count/LOT: %i %i %i", mailInfo.itemID, mailInfo.itemCount, mailInfo.itemLOT); - - if (inv && mailInfo.itemLOT != 0 && mailInfo.itemCount > 0 && item) { - LOG("Trying to remove item with ID/count/LOT: %i %i %i", mailInfo.itemID, mailInfo.itemCount, mailInfo.itemLOT); - inv->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, INVALID, true); - - auto* missionCompoent = player->GetComponent(); - - if (missionCompoent != nullptr) { - missionCompoent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount); - } - } - - character->SaveXMLToDatabase(); + response.Send(sysAddr); } void SendResponse::Serialize(RakNet::BitStream& bitStream) const { MailLUBitStream::Serialize(bitStream); - bitStream.Write(response); + bitStream.Write(status); } void NotificationResponse::Serialize(RakNet::BitStream& bitStream) const { MailLUBitStream::Serialize(bitStream); - LOG("notification: %s", StringifiedEnum::ToString(notification).data()); - bitStream.Write(notification); + bitStream.Write(status); bitStream.Write(0); // unused bitStream.Write(0); // unused - LOG("auctionID: %llu", auctionID); bitStream.Write(auctionID); bitStream.Write(0); // unused - LOG("mailCount: %i", mailCount); bitStream.Write(mailCount); + bitStream.Write(0); // packing } void DataRequest::Handle() { - LOG("DataRequest::Handle()"); auto playerMail = Database::Get()->GetMailForPlayer(static_cast(player->GetObjectID()), 20); - LOG("DataRequest::Handle() - Got %i mail", playerMail.size()); DataResponse response; response.playerMail = playerMail; response.Send(sysAddr); @@ -183,10 +160,8 @@ namespace Mail { void DataResponse::Serialize(RakNet::BitStream& bitStream) const { MailLUBitStream::Serialize(bitStream); - LOG("throtttled: %i", throttled); bitStream.Write(this->throttled); - LOG("playerMail.size(): %i", this->playerMail.size()); bitStream.Write(this->playerMail.size()); bitStream.Write(0); // packing for (const auto& mail : this->playerMail) { @@ -195,32 +170,30 @@ namespace Mail { } bool AttachmentCollectRequest::Deserialize(RakNet::BitStream& bitStream) { + uint32_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); VALIDATE_READ(bitStream.Read(mailID)); VALIDATE_READ(bitStream.Read(playerID)); return true; } void AttachmentCollectRequest::Handle() { - if (mailID > 0 && playerID == player->GetObjectID()) { - auto playerMail = Database::Get()->GetMail(mailID); - - LOT attachmentLOT = 0; - uint32_t attachmentCount = 0; + auto response = AttachmentCollectResponse(eAttachmentCollectResponse::UnknownError); + auto inv = player->GetComponent(); - if (playerMail) { - attachmentLOT = playerMail->itemLOT; - attachmentCount = playerMail->itemCount; + if (mailID > 0 && playerID == player->GetObjectID() && inv) { + auto playerMail = Database::Get()->GetMail(mailID); + if (!playerMail) { + response.status = eAttachmentCollectResponse::MailNotFound; + } else if (!inv->HasSpaceForLoot({ {playerMail->itemLOT, playerMail->itemCount} })) { + response.status = eAttachmentCollectResponse::NoSpaceInInventory; + } else { + inv->AddItem(playerMail->itemLOT, playerMail->itemCount, eLootSourceType::MAIL); + Database::Get()->ClaimMailItem(mailID); + response.status = eAttachmentCollectResponse::Success; } - - auto inv = player->GetComponent(); - if (!inv) return; - - inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL); - - Database::Get()->ClaimMailItem(mailID); - - AttachmentCollectResponse(eRemoveAttachmentResponse::Success, mailID).Send(sysAddr); } + response.Send(sysAddr); } void AttachmentCollectResponse::Serialize(RakNet::BitStream& bitStream) const { @@ -230,16 +203,24 @@ namespace Mail { } bool DeleteRequest::Deserialize(RakNet::BitStream& bitStream) { + int32_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); VALIDATE_READ(bitStream.Read(mailID)); VALIDATE_READ(bitStream.Read(playerID)); return true; } void DeleteRequest::Handle() { - DeleteResponse response(mailID); - if (mailID > 0) { + auto response = DeleteResponse(mailID); + + auto mailData = Database::Get()->GetMail(mailID); + if (mailData && !(mailData->itemLOT != 0 && mailData->itemCount > 0)) { Database::Get()->DeleteMail(mailID); response.status = eDeleteResponse::Success; + } else if (mailData && mailData->itemLOT != 0 && mailData->itemCount > 0) { + response.status = eDeleteResponse::HasAttachments; + } else { + response.status = eDeleteResponse::NotFound; } response.Send(sysAddr); } @@ -258,12 +239,12 @@ namespace Mail { } void ReadRequest::Handle() { - ReadResponse response; - response.mailID = mailID; - response.status = eReadResponse::Success; + auto response = ReadResponse(mailID); - if (mailID > 0) Database::Get()->MarkMailRead(mailID); - else response.status = eReadResponse::UnknownError; + if (Database::Get()->GetMail(mailID)) { + response.status = eReadResponse::Success; + Database::Get()->MarkMailRead(mailID); + } response.Send(sysAddr); } @@ -274,14 +255,14 @@ namespace Mail { } void NotificationRequest::Handle() { + auto response = NotificationResponse(eNotificationResponse::UnknownError); auto character = player->GetCharacter(); - if (!character) { - NotificationResponse(eNotificationResponse::UnknownError, 0).Send(sysAddr); - return; + if (character) { + auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID()); + response.status = eNotificationResponse::NewMail; + response.mailCount = unreadMailCount; } - - auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID()); - if (unreadMailCount > 0) NotificationResponse(eNotificationResponse::NewMail, unreadMailCount).Send(sysAddr); + response.Send(sysAddr); } } diff --git a/dGame/dUtilities/Mail.h b/dGame/dUtilities/Mail.h index ab60c2019..2ca5d3b91 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -55,13 +55,13 @@ namespace Mail { enum class eDeleteResponse : uint32_t { Success = 0, - HasAttachements, + HasAttachments, NotFound, Throttled, UnknownError }; - enum class eRemoveAttachmentResponse : uint32_t { + enum class eAttachmentCollectResponse : uint32_t { Success = 0, AttachmentNotFound, NoSpaceInInventory, @@ -87,6 +87,22 @@ namespace Mail { UnknownError }; + enum class eAuctionCreateResponse : uint32_t { + Success = 0, + NotEnoughMoney, + ItemNotFound, + ItemNotSellable, + UnknownError + }; + + enum class eAuctionCancelResponse : uint32_t { + NotFound = 0, + NotYours, + HasBid, + NoLongerExists, + UnknownError + }; + struct MailLUBitStream : public LUBitStream { eMessageID messageID = eMessageID::UnknownError; SystemAddress sysAddr = UNASSIGNED_SYSTEM_ADDRESS; @@ -108,18 +124,18 @@ namespace Mail { }; struct SendResponse :public MailLUBitStream { - eSendResponse response = eSendResponse::UnknownError; + eSendResponse status = eSendResponse::UnknownError; - SendResponse(eSendResponse _response) : MailLUBitStream(eMessageID::SendResponse), response{_response} {}; + SendResponse(eSendResponse _status) : MailLUBitStream(eMessageID::SendResponse), status{_status} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; struct NotificationResponse : public MailLUBitStream { - eNotificationResponse notification = eNotificationResponse::UnknownError; + eNotificationResponse status = eNotificationResponse::UnknownError; LWOOBJID auctionID = LWOOBJID_EMPTY; uint32_t mailCount = 1; - NotificationResponse(eNotificationResponse _notification) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification} {}; - NotificationResponse(eNotificationResponse _notification, uint32_t _mailCount) : MailLUBitStream(eMessageID::NotificationResponse), notification{_notification}, mailCount{_mailCount} {}; + NotificationResponse(eNotificationResponse _status) : MailLUBitStream(eMessageID::NotificationResponse), status{_status} {}; + NotificationResponse(eNotificationResponse _status, uint32_t _mailCount) : MailLUBitStream(eMessageID::NotificationResponse), status{_status}, mailCount{_mailCount} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -132,7 +148,7 @@ namespace Mail { uint32_t throttled = 0; std::vector playerMail; - DataResponse() : MailLUBitStream(eMessageID::DataRequest) {}; + DataResponse() : MailLUBitStream(eMessageID::DataResponse) {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -147,10 +163,10 @@ namespace Mail { }; struct AttachmentCollectResponse : public MailLUBitStream { - eRemoveAttachmentResponse status = eRemoveAttachmentResponse::UnknownError; + eAttachmentCollectResponse status = eAttachmentCollectResponse::UnknownError; uint64_t mailID = 0; - - AttachmentCollectResponse(eRemoveAttachmentResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status}, mailID{_mailID} {}; + AttachmentCollectResponse(eAttachmentCollectResponse _status) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status} {}; + AttachmentCollectResponse(eAttachmentCollectResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status}, mailID{_mailID} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -168,6 +184,7 @@ namespace Mail { eDeleteResponse status = eDeleteResponse::UnknownError; uint64_t mailID = 0; DeleteResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::DeleteResponse), mailID{_mailID} {}; + DeleteResponse(eDeleteResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::DeleteResponse), status{_status}, mailID{_mailID} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -184,6 +201,7 @@ namespace Mail { eReadResponse status = eReadResponse::UnknownError; ReadResponse() : MailLUBitStream(eMessageID::ReadResponse) {}; + ReadResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::ReadResponse), mailID{_mailID} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index becdcdd4e..da7da9458 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -996,7 +996,7 @@ void SlashCommandHandler::Startup() { Command RequestMailCountCommand{ .help = "Gets the players mail count", .info = "Sends notification with number of unread messages in the player's mailbox", - .aliases = { "requestmailcount" }, + .aliases = { "requestmailcount", "checkmail" }, .handle = GMZeroCommands::RequestMailCount, .requiredLevel = eGameMasterLevel::CIVILIAN }; diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index fcac8143a..89ecef058 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -348,7 +348,7 @@ int main(int argc, char** argv) { StartChatServer(); Game::im->GetInstance(0, false, 0); - Game::im->GetInstance(1100, false, 0); + Game::im->GetInstance(1000, false, 0); StartAuthServer(); } diff --git a/dNet/BitStreamUtils.cpp b/dNet/BitStreamUtils.cpp index 8fc5d20dc..047a1227c 100644 --- a/dNet/BitStreamUtils.cpp +++ b/dNet/BitStreamUtils.cpp @@ -26,7 +26,5 @@ void LUBitStream::Send(const SystemAddress& sysAddr) const { RakNet::BitStream bitStream; this->WriteHeader(bitStream); this->Serialize(bitStream); - LOG("%s", sysAddr.ToString(true)); - PacketUtils::SavePacket("mailv2", reinterpret_cast(bitStream.GetData()), bitStream.GetNumberOfBytesUsed()); Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); } diff --git a/dNet/MailInfo.cpp b/dNet/MailInfo.cpp index 6ca1de474..335b46437 100644 --- a/dNet/MailInfo.cpp +++ b/dNet/MailInfo.cpp @@ -3,33 +3,26 @@ #include "DluAssert.h" void MailInfo::Serialize(RakNet::BitStream& bitStream) const { - LOG("Writing MailInfo"); - LOG("ID: %llu", id); bitStream.Write(id); - LOG("Subject: %s", subject.c_str()); const LUWString subject(this->subject, 50); bitStream.Write(subject); - LOG("Body: %s", body.c_str()); const LUWString body(this->body, 400); bitStream.Write(body); - LOG("Sender: %s", senderUsername.c_str()); const LUWString sender(this->senderUsername, 32); bitStream.Write(sender); bitStream.Write(0); // packing bitStream.Write(0); // attachedCurrency - LOG("ItemID: %llu", itemID); bitStream.Write(itemID); LOT lot = itemLOT; - LOG("ItemLOT: %u", lot); if (lot <= 0) bitStream.Write(LOT_NULL); else bitStream.Write(lot); bitStream.Write(0); // packing bitStream.Write(itemSubkey); - bitStream.Write(itemCount); + bitStream.Write(itemCount); bitStream.Write(0); // subject type (used for auction) bitStream.Write(0); // packing bitStream.Write(0); // packing @@ -39,13 +32,11 @@ void MailInfo::Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(wasRead); // was read bitStream.Write(0); // isLocalized - bitStream.Write(0); // packing + bitStream.Write(1033); // language code bitStream.Write(0); // packing } bool MailInfo::Deserialize(RakNet::BitStream& bitStream) { - VALIDATE_READ(bitStream.Read(id)); - LUWString subject(50); VALIDATE_READ(bitStream.Read(subject)); this->subject = subject.GetAsString(); @@ -54,36 +45,17 @@ bool MailInfo::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(body)); this->body = body.GetAsString(); - LUWString sender(32); - VALIDATE_READ(bitStream.Read(sender)); - this->senderUsername = sender.GetAsString(); + LUWString recipientName(32); + VALIDATE_READ(bitStream.Read(recipientName)); + this->recipient = recipientName.GetAsString(); - bitStream.IgnoreBytes(4); // packing + uint64_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); - bitStream.IgnoreBytes(8); // attachedCurrency VALIDATE_READ(bitStream.Read(itemID)); - - LOT lot; - VALIDATE_READ(bitStream.Read(lot)); - if (lot == LOT_NULL) itemLOT = 0; - else itemLOT = lot; - bitStream.IgnoreBytes(4); // packing - - VALIDATE_READ(bitStream.Read(itemSubkey)); - VALIDATE_READ(bitStream.Read(itemCount)); - - bitStream.IgnoreBytes(1); // subject type (used for auction) - bitStream.IgnoreBytes(1); // packing - bitStream.IgnoreBytes(4); // packing - - VALIDATE_READ(bitStream.Read(timeSent)); // expiration date - VALIDATE_READ(bitStream.Read(timeSent)); // send date - VALIDATE_READ(bitStream.Read(wasRead)); // was read - - bitStream.IgnoreBytes(1); // isLocalized - bitStream.IgnoreBytes(2); // packing - bitStream.IgnoreBytes(4); // packing + VALIDATE_READ(bitStream.Read(languageCode)); + bitStream.IgnoreBytes(4); // padding DluAssert(bitStream.GetNumberOfUnreadBits() == 0); diff --git a/dNet/MailInfo.h b/dNet/MailInfo.h index 2159c0719..c74eee879 100644 --- a/dNet/MailInfo.h +++ b/dNet/MailInfo.h @@ -19,9 +19,10 @@ struct MailInfo { uint32_t receiverId{}; uint64_t timeSent{}; bool wasRead{}; + uint16_t languageCode{}; struct { LWOOBJID itemID{}; - int32_t itemCount{}; + int16_t itemCount{}; LOT itemLOT{}; LWOOBJID itemSubkey{}; }; From b388b0325155dbbe7c986546cb40900f3d119ec7 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sat, 1 Feb 2025 01:21:17 -0600 Subject: [PATCH 6/9] remove fwd decl --- dDatabase/GameDatabase/ITables/IMail.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dDatabase/GameDatabase/ITables/IMail.h b/dDatabase/GameDatabase/ITables/IMail.h index e77f9fa22..239f3cf20 100644 --- a/dDatabase/GameDatabase/ITables/IMail.h +++ b/dDatabase/GameDatabase/ITables/IMail.h @@ -9,11 +9,7 @@ #include "NiQuaternion.h" #include "NiPoint3.h" #include "MailInfo.h" - -namespace RakNet { - class BitStream; -} - + class IMail { public: // Insert a new mail into the database. From 78e52904e5c943fdead6fe35ece6e853600a9639 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sat, 1 Feb 2025 01:51:46 -0600 Subject: [PATCH 7/9] address feedback --- dGame/dUtilities/Mail.cpp | 19 +++++++++++-------- dGame/dUtilities/Mail.h | 13 +++---------- .../SlashCommands/GMZeroCommands.cpp | 5 ++++- dNet/MailInfo.cpp | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index 836628c6a..43be2fa3a 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -70,7 +70,7 @@ namespace Mail { } void SendRequest::Handle() { - SendResponse response(eSendResponse::UnknownError); + SendResponse response; auto* character = player->GetCharacter(); if (character && !(character->HasPermission(ePermissionMap::RestrictedMailAccess) || character->GetParentUser()->GetIsMuted())) { mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), ""); @@ -178,7 +178,7 @@ namespace Mail { } void AttachmentCollectRequest::Handle() { - auto response = AttachmentCollectResponse(eAttachmentCollectResponse::UnknownError); + AttachmentCollectResponse response; auto inv = player->GetComponent(); if (mailID > 0 && playerID == player->GetObjectID() && inv) { @@ -211,8 +211,9 @@ namespace Mail { } void DeleteRequest::Handle() { - auto response = DeleteResponse(mailID); - + DeleteResponse response; + response.mailID = mailID; + auto mailData = Database::Get()->GetMail(mailID); if (mailData && !(mailData->itemLOT != 0 && mailData->itemCount > 0)) { Database::Get()->DeleteMail(mailID); @@ -239,7 +240,8 @@ namespace Mail { } void ReadRequest::Handle() { - auto response = ReadResponse(mailID); + ReadResponse response; + response.mailID = mailID; if (Database::Get()->GetMail(mailID)) { response.status = eReadResponse::Success; @@ -255,7 +257,7 @@ namespace Mail { } void NotificationRequest::Handle() { - auto response = NotificationResponse(eNotificationResponse::UnknownError); + NotificationResponse response; auto character = player->GetCharacter(); if (character) { auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID()); @@ -352,6 +354,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJ Database::Get()->InsertNewMail(mailInsert); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server - - NotificationResponse(eNotificationResponse::NewMail).Send(sysAddr); + NotificationResponse response; + response.status = eNotificationResponse::NewMail; + response.Send(sysAddr); } diff --git a/dGame/dUtilities/Mail.h b/dGame/dUtilities/Mail.h index 2ca5d3b91..f42735433 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -125,8 +125,6 @@ namespace Mail { struct SendResponse :public MailLUBitStream { eSendResponse status = eSendResponse::UnknownError; - - SendResponse(eSendResponse _status) : MailLUBitStream(eMessageID::SendResponse), status{_status} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -134,8 +132,7 @@ namespace Mail { eNotificationResponse status = eNotificationResponse::UnknownError; LWOOBJID auctionID = LWOOBJID_EMPTY; uint32_t mailCount = 1; - NotificationResponse(eNotificationResponse _status) : MailLUBitStream(eMessageID::NotificationResponse), status{_status} {}; - NotificationResponse(eNotificationResponse _status, uint32_t _mailCount) : MailLUBitStream(eMessageID::NotificationResponse), status{_status}, mailCount{_mailCount} {}; + NotificationResponse() : MailLUBitStream(eMessageID::NotificationResponse) {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -165,8 +162,7 @@ namespace Mail { struct AttachmentCollectResponse : public MailLUBitStream { eAttachmentCollectResponse status = eAttachmentCollectResponse::UnknownError; uint64_t mailID = 0; - AttachmentCollectResponse(eAttachmentCollectResponse _status) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status} {}; - AttachmentCollectResponse(eAttachmentCollectResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::AttachmentCollectResponse), status{_status}, mailID{_mailID} {}; + AttachmentCollectResponse() : MailLUBitStream(eMessageID::AttachmentCollectResponse) {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -174,7 +170,6 @@ namespace Mail { uint64_t mailID = 0; LWOOBJID playerID = LWOOBJID_EMPTY; - DeleteRequest() : MailLUBitStream(eMessageID::DeleteRequest) {}; bool Deserialize(RakNet::BitStream& bitStream) override; void Handle() override; @@ -183,8 +178,7 @@ namespace Mail { struct DeleteResponse : public MailLUBitStream { eDeleteResponse status = eDeleteResponse::UnknownError; uint64_t mailID = 0; - DeleteResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::DeleteResponse), mailID{_mailID} {}; - DeleteResponse(eDeleteResponse _status, uint64_t _mailID) : MailLUBitStream(eMessageID::DeleteResponse), status{_status}, mailID{_mailID} {}; + DeleteResponse() : MailLUBitStream(eMessageID::DeleteResponse) {}; void Serialize(RakNet::BitStream& bitStream) const override; }; @@ -201,7 +195,6 @@ namespace Mail { eReadResponse status = eReadResponse::UnknownError; ReadResponse() : MailLUBitStream(eMessageID::ReadResponse) {}; - ReadResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::ReadResponse), mailID{_mailID} {}; void Serialize(RakNet::BitStream& bitStream) const override; }; diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 6b9e62019..f80f32803 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -217,7 +217,10 @@ namespace GMZeroCommands { } void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - Mail::NotificationResponse(Mail::eNotificationResponse::NewMail, Database::Get()->GetUnreadMailCount(entity->GetCharacter()->GetID())).Send(sysAddr); + Mail::NotificationResponse response; + response.status = Mail::eNotificationResponse::NewMail; + response.mailCount = Database::Get()->GetUnreadMailCount(entity->GetCharacter()->GetID()); + response.Send(sysAddr); } void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { diff --git a/dNet/MailInfo.cpp b/dNet/MailInfo.cpp index 335b46437..1556fc061 100644 --- a/dNet/MailInfo.cpp +++ b/dNet/MailInfo.cpp @@ -60,4 +60,4 @@ bool MailInfo::Deserialize(RakNet::BitStream& bitStream) { DluAssert(bitStream.GetNumberOfUnreadBits() == 0); return true; -} \ No newline at end of file +} From 6eaf0a153e2e95cf03c11c714a2208a536e20049 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sat, 1 Feb 2025 01:56:57 -0600 Subject: [PATCH 8/9] explicit character ID usage --- dGame/dUtilities/Mail.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index 43be2fa3a..da83a6f05 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -152,7 +152,7 @@ namespace Mail { } void DataRequest::Handle() { - auto playerMail = Database::Get()->GetMailForPlayer(static_cast(player->GetObjectID()), 20); + auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20); DataResponse response; response.playerMail = playerMail; response.Send(sysAddr); From 14d7dec6a8b2c476bf11dbc26f8b2d89886bb873 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Sat, 1 Feb 2025 02:05:17 -0600 Subject: [PATCH 9/9] toctou --- dGame/dUtilities/Mail.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index da83a6f05..d00d47e84 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -152,7 +152,9 @@ namespace Mail { } void DataRequest::Handle() { - auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20); + const auto* character = player->GetCharacter(); + if (!character) return; + auto playerMail = Database::Get()->GetMailForPlayer(character->GetID(), 20); DataResponse response; response.playerMail = playerMail; response.Send(sysAddr);