mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-04-26 16:46:31 +00:00
363 lines
11 KiB
C++
363 lines
11 KiB
C++
#include "Mail.h"
|
|
#include <functional>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <regex>
|
|
#include <time.h>
|
|
#include <future>
|
|
|
|
#include "GeneralUtils.h"
|
|
#include "Database.h"
|
|
#include "Game.h"
|
|
#include "dServer.h"
|
|
#include "Entity.h"
|
|
#include "Character.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "Logger.h"
|
|
#include "EntityManager.h"
|
|
#include "InventoryComponent.h"
|
|
#include "GameMessages.h"
|
|
#include "Item.h"
|
|
#include "MissionComponent.h"
|
|
#include "ChatPackets.h"
|
|
#include "Character.h"
|
|
#include "dZoneManager.h"
|
|
#include "WorldConfig.h"
|
|
#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,
|
|
DefaultSender,
|
|
recipient->GetObjectID(),
|
|
recipient->GetCharacter()->GetName(),
|
|
subject,
|
|
body,
|
|
attachment,
|
|
attachmentCount,
|
|
recipient->GetSystemAddress()
|
|
);
|
|
}
|
|
|
|
void Mail::SendMail(const LWOOBJID recipient, const std::string& recipientName, const std::string& subject,
|
|
const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
|
SendMail(
|
|
LWOOBJID_EMPTY,
|
|
DefaultSender,
|
|
recipient,
|
|
recipientName,
|
|
subject,
|
|
body,
|
|
attachment,
|
|
attachmentCount,
|
|
sysAddr
|
|
);
|
|
}
|
|
|
|
void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, const Entity* recipient, const std::string& subject,
|
|
const std::string& body, const LOT attachment, const uint16_t attachmentCount) {
|
|
SendMail(
|
|
sender,
|
|
senderName,
|
|
recipient->GetObjectID(),
|
|
recipient->GetCharacter()->GetName(),
|
|
subject,
|
|
body,
|
|
attachment,
|
|
attachmentCount,
|
|
recipient->GetSystemAddress()
|
|
);
|
|
}
|
|
|
|
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) {
|
|
MailInfo mailInsert;
|
|
mailInsert.senderUsername = senderName;
|
|
mailInsert.recipient = recipientName;
|
|
mailInsert.subject = subject;
|
|
mailInsert.body = body;
|
|
mailInsert.senderId = sender;
|
|
mailInsert.receiverId = recipient;
|
|
mailInsert.itemCount = attachmentCount;
|
|
mailInsert.itemID = LWOOBJID_EMPTY;
|
|
mailInsert.itemLOT = attachment;
|
|
mailInsert.itemSubkey = LWOOBJID_EMPTY;
|
|
|
|
Database::Get()->InsertNewMail(mailInsert);
|
|
|
|
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server
|
|
NotificationResponse response;
|
|
response.status = eNotificationResponse::NewMail;
|
|
response.Send(sysAddr);
|
|
}
|