mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-04 14:42:02 +00:00 
			
		
		
		
	Merge branch 'mailv2' into websockets
This commit is contained in:
		@@ -7,7 +7,8 @@ enum class eConnectionType : uint16_t {
 | 
			
		||||
	CHAT,
 | 
			
		||||
	WORLD = 4,
 | 
			
		||||
	CLIENT,
 | 
			
		||||
	MASTER
 | 
			
		||||
	MASTER,
 | 
			
		||||
	UNKNOWN
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif  //!__ECONNECTIONTYPE__H__
 | 
			
		||||
 
 | 
			
		||||
@@ -8,27 +8,10 @@
 | 
			
		||||
#include "dCommonVars.h"
 | 
			
		||||
#include "NiQuaternion.h"
 | 
			
		||||
#include "NiPoint3.h"
 | 
			
		||||
 | 
			
		||||
#include "MailInfo.h"
 | 
			
		||||
 
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -79,14 +79,14 @@ public:
 | 
			
		||||
	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<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
 | 
			
		||||
	std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<MailInfo> 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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<IMail::MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
 | 
			
		||||
std::vector<MailInfo> 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<IMail::MailInfo> toReturn;
 | 
			
		||||
	std::vector<MailInfo> 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<IMail::MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t char
 | 
			
		||||
	return toReturn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<IMail::MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
 | 
			
		||||
std::optional<MailInfo> 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");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,14 +77,14 @@ public:
 | 
			
		||||
	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<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
 | 
			
		||||
	std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<MailInfo> 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
 | 
			
		||||
std::vector<MailInfo> 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<IMail::MailInfo> toReturn;
 | 
			
		||||
	std::vector<MailInfo> 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<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t cha
 | 
			
		||||
	return toReturn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<IMail::MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
 | 
			
		||||
std::optional<MailInfo> 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");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<IMail::MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
 | 
			
		||||
std::vector<MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<IMail::MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
 | 
			
		||||
std::optional<MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
 | 
			
		||||
	std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
 | 
			
		||||
	std::optional<MailInfo> 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -828,7 +828,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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,278 @@
 | 
			
		||||
#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<eMessageID, std::function<std::unique_ptr<MailLUBitStream>()>> g_Handlers = {
 | 
			
		||||
		{eMessageID::SendRequest, []() {
 | 
			
		||||
			return std::make_unique<SendRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
		{eMessageID::DataRequest, []() {
 | 
			
		||||
			return std::make_unique<DataRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
		{eMessageID::AttachmentCollectRequest, []() {
 | 
			
		||||
			return std::make_unique<AttachmentCollectRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
		{eMessageID::DeleteRequest, []() {
 | 
			
		||||
			return std::make_unique<DeleteRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
		{eMessageID::ReadRequest, []() {
 | 
			
		||||
			return std::make_unique<ReadRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
		{eMessageID::NotificationRequest, []() {
 | 
			
		||||
			return std::make_unique<NotificationRequest>();
 | 
			
		||||
		}},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
		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<InventoryComponent>();
 | 
			
		||||
				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<MissionComponent>();
 | 
			
		||||
						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<uint64_t>(0); // unused
 | 
			
		||||
		bitStream.Write<uint64_t>(0); // unused
 | 
			
		||||
		bitStream.Write(auctionID);
 | 
			
		||||
		bitStream.Write<uint64_t>(0); // unused
 | 
			
		||||
		bitStream.Write(mailCount);
 | 
			
		||||
		bitStream.Write<uint32_t>(0); // packing
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void DataRequest::Handle() {
 | 
			
		||||
		const auto* character = player->GetCharacter();
 | 
			
		||||
		if (!character) return;
 | 
			
		||||
		auto playerMail = Database::Get()->GetMailForPlayer(character->GetID(), 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<uint16_t>(this->playerMail.size());
 | 
			
		||||
		bitStream.Write<uint16_t>(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() {
 | 
			
		||||
		AttachmentCollectResponse response;
 | 
			
		||||
		auto inv = player->GetComponent<InventoryComponent>();
 | 
			
		||||
 | 
			
		||||
		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() {
 | 
			
		||||
		DeleteResponse response;
 | 
			
		||||
		response.mailID = 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() {
 | 
			
		||||
		ReadResponse response;
 | 
			
		||||
		response.mailID = 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() {
 | 
			
		||||
		NotificationResponse response;
 | 
			
		||||
		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 +312,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 +341,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;
 | 
			
		||||
@@ -90,316 +356,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
 | 
			
		||||
 | 
			
		||||
	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<uint32_t>(attachmentID);
 | 
			
		||||
	LOT itemLOT = 0;
 | 
			
		||||
	//Inventory::InventoryType itemType;
 | 
			
		||||
	int mailCost = Game::zoneManager->GetWorldConfig()->mailBaseFee;
 | 
			
		||||
	int stackSize = 0;
 | 
			
		||||
	auto inv = static_cast<InventoryComponent*>(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<MissionComponent>();
 | 
			
		||||
 | 
			
		||||
		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<uint16_t>(playerMail.size()); // size
 | 
			
		||||
	bitStream.Write<uint16_t>(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<uint16_t>(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<uint64_t>(mail.timeSent); // expiration date
 | 
			
		||||
		bitStream.Write<uint64_t>(mail.timeSent);// send date
 | 
			
		||||
		bitStream.Write<uint8_t>(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<InventoryComponent>();
 | 
			
		||||
		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 response;
 | 
			
		||||
	response.status = eNotificationResponse::NewMail;
 | 
			
		||||
	response.Send(sysAddr);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +1,210 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#ifndef __MAIL_H__
 | 
			
		||||
#define __MAIL_H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#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 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 MailSendResponse {
 | 
			
		||||
	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
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const std::string ServerName = "Darkflame Universe";
 | 
			
		||||
	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;
 | 
			
		||||
		void Serialize(RakNet::BitStream& bitStream) const override;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct NotificationResponse : public MailLUBitStream {
 | 
			
		||||
		eNotificationResponse status = eNotificationResponse::UnknownError;
 | 
			
		||||
		LWOOBJID auctionID = LWOOBJID_EMPTY;
 | 
			
		||||
		uint32_t mailCount = 1;
 | 
			
		||||
		NotificationResponse() : MailLUBitStream(eMessageID::NotificationResponse) {};
 | 
			
		||||
		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<MailInfo> 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() : MailLUBitStream(eMessageID::AttachmentCollectResponse) {};
 | 
			
		||||
		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() : MailLUBitStream(eMessageID::DeleteResponse) {};
 | 
			
		||||
		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) {};
 | 
			
		||||
		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 +245,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__
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
	};
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,7 @@ namespace GMGreaterThanZeroCommands {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		IMail::MailInfo mailInsert;
 | 
			
		||||
		MailInfo mailInsert;
 | 
			
		||||
		mailInsert.senderId = entity->GetObjectID();
 | 
			
		||||
		mailInsert.senderUsername = "Darkflame Universe";
 | 
			
		||||
		mailInsert.receiverId = receiverID;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
#include "VanityUtilities.h"
 | 
			
		||||
#include "WorldPackets.h"
 | 
			
		||||
#include "ZoneInstanceManager.h"
 | 
			
		||||
#include "Database.h"
 | 
			
		||||
 | 
			
		||||
// Components
 | 
			
		||||
#include "BuffComponent.h"
 | 
			
		||||
@@ -216,7 +217,10 @@ namespace GMZeroCommands {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
 | 
			
		||||
		Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID());
 | 
			
		||||
		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) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								dNet/BitStreamUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								dNet/BitStreamUtils.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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<MessageID>(ID_USER_PACKET_ENUM);
 | 
			
		||||
	bitStream.Write(this->connectionType);
 | 
			
		||||
	bitStream.Write(this->internalPacketID);
 | 
			
		||||
	bitStream.Write<uint8_t>(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<uint8_t>(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);
 | 
			
		||||
}
 | 
			
		||||
@@ -2,12 +2,13 @@
 | 
			
		||||
#define __BITSTREAMUTILS__H__
 | 
			
		||||
 | 
			
		||||
#include "GeneralUtils.h"
 | 
			
		||||
#include "MessageIdentifiers.h"
 | 
			
		||||
#include "BitStream.h"
 | 
			
		||||
#include "MessageIdentifiers.h"
 | 
			
		||||
#include "eConnectionType.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
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 <typename T> 
 | 
			
		||||
	LUBitStream(eConnectionType connectionType, T internalPacketID) {
 | 
			
		||||
		this->connectionType = connectionType;
 | 
			
		||||
		this->internalPacketID = static_cast<uint32_t>(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<typename T>
 | 
			
		||||
	void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) {
 | 
			
		||||
@@ -53,7 +76,6 @@ namespace BitStreamUtils {
 | 
			
		||||
		bitStream.Write(static_cast<uint32_t>(internalPacketID));
 | 
			
		||||
		bitStream.Write<uint8_t>(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace RakNet {
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								dNet/MailInfo.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								dNet/MailInfo.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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<uint32_t>(0); // packing
 | 
			
		||||
 | 
			
		||||
	bitStream.Write<uint64_t>(0); // attachedCurrency
 | 
			
		||||
	bitStream.Write(itemID);
 | 
			
		||||
 | 
			
		||||
	LOT lot = itemLOT;
 | 
			
		||||
	if (lot <= 0) bitStream.Write<LOT>(LOT_NULL);
 | 
			
		||||
	else bitStream.Write(lot);
 | 
			
		||||
	bitStream.Write<uint32_t>(0); // packing
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(itemSubkey);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(itemCount);
 | 
			
		||||
	bitStream.Write<uint8_t>(0); // subject type (used for auction)
 | 
			
		||||
	bitStream.Write<uint8_t>(0); // packing
 | 
			
		||||
	bitStream.Write<uint32_t>(0); //  packing
 | 
			
		||||
 | 
			
		||||
	bitStream.Write<uint64_t>(timeSent); // expiration date
 | 
			
		||||
	bitStream.Write<uint64_t>(timeSent);// send date
 | 
			
		||||
	bitStream.Write<uint8_t>(wasRead); // was read
 | 
			
		||||
 | 
			
		||||
	bitStream.Write<uint8_t>(0); // isLocalized
 | 
			
		||||
	bitStream.Write<uint16_t>(1033); // language code
 | 
			
		||||
	bitStream.Write<uint32_t>(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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								dNet/MailInfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								dNet/MailInfo.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#ifndef __MAILINFO_H__
 | 
			
		||||
#define __MAILINFO_H__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#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__
 | 
			
		||||
@@ -848,15 +848,20 @@ void HandlePacket(Packet* packet) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (packet->data[0] != ID_USER_PACKET_ENUM || packet->length < 4) return;
 | 
			
		||||
	if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) {
 | 
			
		||||
		if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
 | 
			
		||||
 | 
			
		||||
	CINSTREAM;
 | 
			
		||||
	LUBitStream luBitStream;
 | 
			
		||||
	luBitStream.ReadHeader(inStream);
 | 
			
		||||
 | 
			
		||||
	if (luBitStream.connectionType == eConnectionType::SERVER) {
 | 
			
		||||
		if (static_cast<MessageType::Server>(luBitStream.internalPacketID) == MessageType::Server::VERSION_CONFIRM) {
 | 
			
		||||
			AuthPackets::HandleHandshake(Game::server, packet);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (static_cast<eConnectionType>(packet->data[1]) != eConnectionType::WORLD) return;
 | 
			
		||||
	if (luBitStream.connectionType != eConnectionType::WORLD) return;
 | 
			
		||||
 | 
			
		||||
	switch (static_cast<MessageType::World>(packet->data[3])) {
 | 
			
		||||
	switch (static_cast<MessageType::World>(luBitStream.internalPacketID)) {
 | 
			
		||||
	case MessageType::World::VALIDATION: {
 | 
			
		||||
		CINSTREAM_SKIP_HEADER;
 | 
			
		||||
		LUWString username;
 | 
			
		||||
@@ -1202,11 +1207,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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user