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/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 e2ffbf79f..836628c6a 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -26,12 +26,274 @@ #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()>> g_Handlers = { + {eMessageID::SendRequest, []() { + return std::make_unique(); + }}, + {eMessageID::DataRequest, []() { + return std::make_unique(); + }}, + {eMessageID::AttachmentCollectRequest, []() { + return std::make_unique(); + }}, + {eMessageID::DeleteRequest, []() { + return std::make_unique(); + }}, + {eMessageID::ReadRequest, []() { + return std::make_unique(); + }}, + {eMessageID::NotificationRequest, []() { + 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() { + SendResponse response(eSendResponse::UnknownError); + 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]+"), ""); + 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 { + 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; + } + } + } + } else { + response.status = eSendResponse::SenderAccountIsMuted; + } + response.Send(sysAddr); + } + + void SendResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + } + + void NotificationResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(0); // unused + bitStream.Write(0); // unused + bitStream.Write(auctionID); + bitStream.Write(0); // unused + bitStream.Write(mailCount); + bitStream.Write(0); // packing + } + + void DataRequest::Handle() { + auto playerMail = Database::Get()->GetMailForPlayer(static_cast(player->GetObjectID()), 20); + 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) { + uint32_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); + VALIDATE_READ(bitStream.Read(mailID)); + VALIDATE_READ(bitStream.Read(playerID)); + return true; + } + + void AttachmentCollectRequest::Handle() { + auto response = AttachmentCollectResponse(eAttachmentCollectResponse::UnknownError); + auto inv = player->GetComponent(); + + 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; + } + } + response.Send(sysAddr); + } + + void AttachmentCollectResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(mailID); + } + + 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() { + 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); + } + + 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() { + auto response = ReadResponse(mailID); + + if (Database::Get()->GetMail(mailID)) { + response.status = eReadResponse::Success; + Database::Get()->MarkMailRead(mailID); + } + response.Send(sysAddr); + } + + void ReadResponse::Serialize(RakNet::BitStream& bitStream) const { + MailLUBitStream::Serialize(bitStream); + bitStream.Write(status); + bitStream.Write(mailID); + } + + void NotificationRequest::Handle() { + auto response = NotificationResponse(eNotificationResponse::UnknownError); + auto character = player->GetCharacter(); + if (character) { + auto unreadMailCount = Database::Get()->GetUnreadMailCount(character->GetID()); + response.status = eNotificationResponse::NewMail; + response.mailCount = unreadMailCount; + } + response.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 = 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; + } + request->Handle(); + } else { + LOG_DEBUG("Unhandled Mail Request 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 +308,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 +337,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,315 +353,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::HandleMailStuff(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::AccountIsMuted); - - 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; - 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 - } - - 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; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::MAIL); - bitStream.Write(int(MailMessageID::SendResponse)); - bitStream.Write(int(response)); - 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 - 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); - 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); - Game::server->Send(bitStream, sysAddr, false); - - Database::Get()->DeleteMail(mailID); -} - -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); - 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 07c3e37fb..2ca5d3b91 100644 --- a/dGame/dUtilities/Mail.h +++ b/dGame/dUtilities/Mail.h @@ -1,43 +1,217 @@ -#pragma once +#ifndef __MAIL_H__ +#define __MAIL_H__ + +#include #include "BitStream.h" #include "RakNetTypes.h" #include "dCommonVars.h" +#include "BitStreamUtils.h" +#include "MailInfo.h" 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 MailSendResponse { + enum class eMessageID : uint32_t { + SendRequest = 0, + SendResponse, + NotificationResponse, + DataRequest, + DataResponse, + AttachmentCollectRequest, + AttachmentCollectResponse, + DeleteRequest, + DeleteResponse, + ReadRequest, + ReadResponse, + NotificationRequest, + AuctionCreate, + AuctionCreationResponse, + AuctionCancel, + AuctionCancelResponse, + AuctionList, + AuctionListResponse, + AuctionBid, + AuctionBidResponse, + UnknownError + }; + + enum class eSendResponse : 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 eDeleteResponse : uint32_t { + Success = 0, + HasAttachments, + NotFound, + Throttled, + UnknownError + }; + + enum class eAttachmentCollectResponse : uint32_t { + Success = 0, + AttachmentNotFound, + NoSpaceInInventory, + MailNotFound, + Throttled, + UnknownError + }; + + enum class eNotificationResponse : uint32_t { + NewMail = 0, + UnHandled, + AuctionWon, + AuctionSold, + AuctionOutbided, + AuctionExpired, + AuctionCancelled, + AuctionUpdated, + UnknownError + }; + + enum class eReadResponse : uint32_t { + Success = 0, + 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; + Entity* player = nullptr; + + MailLUBitStream() = default; + MailLUBitStream(eMessageID _messageID) : LUBitStream(eConnectionType::CLIENT, MessageType::Client::MAIL), messageID{_messageID} {}; + + 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 status = eSendResponse::UnknownError; + + SendResponse(eSendResponse _status) : MailLUBitStream(eMessageID::SendResponse), status{_status} {}; + void Serialize(RakNet::BitStream& bitStream) const override; }; - const std::string ServerName = "Darkflame Universe"; + struct NotificationResponse : public MailLUBitStream { + 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} {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct DataRequest : public MailLUBitStream { + bool Deserialize(RakNet::BitStream& bitStream) override { return true; }; + void Handle() override; + }; + + struct DataResponse : public MailLUBitStream { + uint32_t throttled = 0; + std::vector playerMail; + + DataResponse() : MailLUBitStream(eMessageID::DataResponse) {}; + void Serialize(RakNet::BitStream& bitStream) const override; + + }; + + struct AttachmentCollectRequest : public MailLUBitStream { + uint64_t mailID = 0; + LWOOBJID playerID = LWOOBJID_EMPTY; + + AttachmentCollectRequest() : MailLUBitStream(eMessageID::AttachmentCollectRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; + }; + + 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} {}; + 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 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} {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct ReadRequest : public MailLUBitStream { + uint64_t mailID = 0; + + ReadRequest() : MailLUBitStream(eMessageID::ReadRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override; + void Handle() override; + }; + + struct ReadResponse : public MailLUBitStream { + uint64_t mailID = 0; + eReadResponse status = eReadResponse::UnknownError; + + ReadResponse() : MailLUBitStream(eMessageID::ReadResponse) {}; + ReadResponse(uint64_t _mailID) : MailLUBitStream(eMessageID::ReadResponse), mailID{_mailID} {}; + void Serialize(RakNet::BitStream& bitStream) const override; + }; + + struct NotificationRequest : public MailLUBitStream { + NotificationRequest() : MailLUBitStream(eMessageID::NotificationRequest) {}; + bool Deserialize(RakNet::BitStream& bitStream) override { return true; }; + void Handle() override; + }; + + void HandleMail(RakNet::BitStream& inStream, const SystemAddress& sysAddr, Entity* player); void SendMail( const Entity* recipient, @@ -78,18 +252,6 @@ namespace Mail { uint16_t attachmentCount, const SystemAddress& sysAddr ); - - void HandleMailStuff(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); }; + +#endif // !__MAIL_H__ 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/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/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index 51fa6e15b..6b9e62019 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->GetCharacter()->GetID())).Send(sysAddr); } void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { diff --git a/dNet/BitStreamUtils.cpp b/dNet/BitStreamUtils.cpp new file mode 100644 index 000000000..047a1227c --- /dev/null +++ b/dNet/BitStreamUtils.cpp @@ -0,0 +1,30 @@ +#include "BitStreamUtils.h" +#include "dServer.h" +#include "BitStream.h" +#include "PacketUtils.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 33fde5642..d9e9bc9b1 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -2,12 +2,13 @@ #define __BITSTREAMUTILS__H__ #include "GeneralUtils.h" -#include "MessageIdentifiers.h" #include "BitStream.h" +#include "MessageIdentifiers.h" +#include "eConnectionType.h" #include #include -enum class eConnectionType : uint16_t; +#define VALIDATE_READ(x) do { if (!x) return false; } while (0) struct LUString { std::string string; @@ -45,6 +46,28 @@ struct LUWString { }; }; +struct LUBitStream { + eConnectionType connectionType = eConnectionType::UNKNOWN; + uint32_t internalPacketID = 0xFFFFFFFF; + + LUBitStream() = default; + + template + LUBitStream(eConnectionType connectionType, T internalPacketID) { + this->connectionType = connectionType; + 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) { @@ -53,7 +76,6 @@ namespace BitStreamUtils { bitStream.Write(static_cast(internalPacketID)); bitStream.Write(0); } - } namespace RakNet { diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index 172aee205..67c2ea1fa 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -1,7 +1,9 @@ set(DNET_SOURCES "AuthPackets.cpp" + "BitStreamUtils.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 new file mode 100644 index 000000000..335b46437 --- /dev/null +++ b/dNet/MailInfo.cpp @@ -0,0 +1,63 @@ +#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(1033); // language code + bitStream.Write(0); // packing +} + +bool MailInfo::Deserialize(RakNet::BitStream& bitStream) { + 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 recipientName(32); + VALIDATE_READ(bitStream.Read(recipientName)); + this->recipient = recipientName.GetAsString(); + + uint64_t unknown; + VALIDATE_READ(bitStream.Read(unknown)); + + VALIDATE_READ(bitStream.Read(itemID)); + VALIDATE_READ(bitStream.Read(itemCount)); + VALIDATE_READ(bitStream.Read(languageCode)); + bitStream.IgnoreBytes(4); // padding + + 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..c74eee879 --- /dev/null +++ b/dNet/MailInfo.h @@ -0,0 +1,34 @@ +#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{}; + uint16_t languageCode{}; + struct { + LWOOBJID itemID{}; + int16_t itemCount{}; + LOT itemLOT{}; + LWOOBJID itemSubkey{}; + }; + + void Serialize(RakNet::BitStream& bitStream) const; + bool Deserialize(RakNet::BitStream& bitStream); +}; + +#endif // __MAILINFO_H__ diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 6a5969600..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::HandleMailStuff(bitStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); + Mail::HandleMail(inStream, packet->systemAddress, UserManager::Instance()->GetUser(packet->systemAddress)->GetLastUsedChar()->GetEntity()); break; }