From 8981ad8c14bdc2c4c22203944a3445ed57ec8c8c Mon Sep 17 00:00:00 2001 From: gsemaj Date: Mon, 15 Mar 2021 10:03:45 -0400 Subject: [PATCH] [refactor] Separate email functions out of BuddyManager into Email --- Makefile | 2 + src/BuddyManager.cpp | 321 ----------------------------------------- src/BuddyManager.hpp | 10 -- src/Email.cpp | 332 +++++++++++++++++++++++++++++++++++++++++++ src/Email.hpp | 23 +++ src/main.cpp | 2 + 6 files changed, 359 insertions(+), 331 deletions(-) create mode 100644 src/Email.cpp create mode 100644 src/Email.hpp diff --git a/Makefile b/Makefile index d5a5e97..d756d44 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ CXXSRC=\ src/CNShared.cpp\ src/Database.cpp\ src/Defines.cpp\ + src/Email.cpp\ src/main.cpp\ src/MissionManager.cpp\ src/MobAI.cpp\ @@ -80,6 +81,7 @@ CXXHDR=\ src/CNStructs.hpp\ src/Database.hpp\ src/Defines.hpp\ + src/Email.hpp\ vendor/INIReader.hpp\ vendor/JSON.hpp\ src/MissionManager.hpp\ diff --git a/src/BuddyManager.cpp b/src/BuddyManager.cpp index 966947e..f79b56b 100644 --- a/src/BuddyManager.cpp +++ b/src/BuddyManager.cpp @@ -22,15 +22,6 @@ void BuddyManager::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_SET_PC_BLOCK, reqPlayerBlock); REGISTER_SHARD_PACKET(P_CL2FE_REQ_REMOVE_BUDDY, reqBuddyDelete); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BUDDY_WARP, reqBuddyWarp); - // - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK, emailUpdateCheck); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST, emailReceivePageList); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_READ_EMAIL, emailRead); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_CANDY, emailReceiveTaros); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM, emailReceiveItemSingle); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL, emailReceiveItemAll); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_DELETE_EMAIL, emailDelete); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SEND_EMAIL, emailSend); } // Refresh buddy list @@ -481,318 +472,6 @@ fail: sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BUDDY_WARP_FAIL, sizeof(sP_FE2CL_REP_PC_BUDDY_WARP_FAIL)); } -void BuddyManager::emailUpdateCheck(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK)) - return; // malformed packet - - INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp); - resp.iNewEmailCnt = Database::getUnreadEmailCount(PlayerManager::getPlayer(sock)->iID); - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_NEW_EMAIL, sizeof(sP_FE2CL_REP_PC_NEW_EMAIL)); -} - -void BuddyManager::emailReceivePageList(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST)) - return; // malformed packet - - sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST*)data->buf; - - INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC, resp); - resp.iPageNum = pkt->iPageNum; - - std::vector emails = Database::getEmails(PlayerManager::getPlayer(sock)->iID, pkt->iPageNum); - for (int i = 0; i < emails.size(); i++) { - // convert each email and load them into the packet - Database::EmailData* email = &emails.at(i); - sEmailInfo* emailInfo = new sEmailInfo(); - emailInfo->iEmailIndex = email->MsgIndex; - emailInfo->iReadFlag = email->ReadFlag; - emailInfo->iItemCandyFlag = email->ItemFlag; - emailInfo->iFromPCUID = email->SenderId; - emailInfo->SendTime = timeStampToStruct(email->SendTime); - emailInfo->DeleteTime = timeStampToStruct(email->DeleteTime); - U8toU16(email->SenderFirstName, emailInfo->szFirstName, sizeof(emailInfo->szFirstName)); - U8toU16(email->SenderLastName, emailInfo->szLastName, sizeof(emailInfo->szLastName)); - U8toU16(email->SubjectLine, emailInfo->szSubject, sizeof(emailInfo->szSubject)); - resp.aEmailInfo[i] = *emailInfo; - } - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC)); -} - -void BuddyManager::emailRead(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_READ_EMAIL)) - return; // malformed packet - - sP_CL2FE_REQ_PC_READ_EMAIL* pkt = (sP_CL2FE_REQ_PC_READ_EMAIL*)data->buf; - - Player* plr = PlayerManager::getPlayer(sock); - - Database::EmailData email = Database::getEmail(plr->iID, pkt->iEmailIndex); - sItemBase* attachments = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); - email.ReadFlag = 1; // mark as read - Database::updateEmailContent(&email); - - INITSTRUCT(sP_FE2CL_REP_PC_READ_EMAIL_SUCC, resp); - resp.iEmailIndex = pkt->iEmailIndex; - resp.iCash = email.Taros; - for (int i = 0; i < 4; i++) { - resp.aItem[i] = attachments[i]; - } - U8toU16(email.MsgBody, (char16_t*)resp.szContent, sizeof(resp.szContent)); - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_READ_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_READ_EMAIL_SUCC)); -} - -void BuddyManager::emailReceiveTaros(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY)) - return; // malformed packet - - sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY*)data->buf; - - Player* plr = PlayerManager::getPlayer(sock); - - Database::EmailData email = Database::getEmail(plr->iID, pkt->iEmailIndex); - // money transfer - plr->money += email.Taros; - email.Taros = 0; - // update Taros in email - Database::updateEmailContent(&email); - - INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC, resp); - resp.iCandy = plr->money; - resp.iEmailIndex = pkt->iEmailIndex; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC)); -} - -void BuddyManager::emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM)) - return; // malformed packet - - sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf; - Player* plr = PlayerManager::getPlayer(sock); - - if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iSlotNum < 1 || pkt->iSlotNum > 4) - return; // sanity check - - // get email item from db and delete it - sItemBase* attachments = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); - sItemBase itemFrom = attachments[pkt->iEmailItemSlot - 1]; - Database::deleteEmailAttachments(plr->iID, pkt->iEmailIndex, pkt->iEmailItemSlot); - - // move item to player inventory - sItemBase& itemTo = plr->Inven[pkt->iSlotNum]; - itemTo.iID = itemFrom.iID; - itemTo.iOpt = itemFrom.iOpt; - itemTo.iTimeLimit = itemFrom.iTimeLimit; - itemTo.iType = itemFrom.iType; - - INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC, resp); - resp.iEmailIndex = pkt->iEmailIndex; - resp.iEmailItemSlot = pkt->iEmailItemSlot; - resp.iSlotNum = pkt->iSlotNum; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC)); - - // update inventory - INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp2); - resp2.eIL = 1; - resp2.iSlotNum = resp.iSlotNum; - resp2.Item = itemTo; - - sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC)); -} - -void BuddyManager::emailReceiveItemAll(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL)) - return; // malformed packet - - sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL*)data->buf; - - // move items to player inventory - Player* plr = PlayerManager::getPlayer(sock); - sItemBase* itemsFrom = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); - for (int i = 0; i < 4; i++) { - int slot = ItemManager::findFreeSlot(plr); - if (slot < 0 || slot >= AINVEN_COUNT) { - INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_FAIL, failResp); - failResp.iEmailIndex = pkt->iEmailIndex; - failResp.iErrorCode = 0; // ??? - break; // sanity check; should never happen - } - - // copy data over - sItemBase itemFrom = itemsFrom[i]; - sItemBase& itemTo = plr->Inven[slot]; - itemTo.iID = itemFrom.iID; - itemTo.iOpt = itemFrom.iOpt; - itemTo.iTimeLimit = itemFrom.iTimeLimit; - itemTo.iType = itemFrom.iType; - - // update inventory - INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp2); - resp2.eIL = 1; - resp2.iSlotNum = slot; - resp2.Item = itemTo; - - sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC)); - } - - // delete all items from db - Database::deleteEmailAttachments(plr->iID, pkt->iEmailIndex, -1); - - INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC, resp); - resp.iEmailIndex = pkt->iEmailIndex; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC)); -} - -void BuddyManager::emailDelete(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_DELETE_EMAIL)) - return; // malformed packet - - sP_CL2FE_REQ_PC_DELETE_EMAIL* pkt = (sP_CL2FE_REQ_PC_DELETE_EMAIL*)data->buf; - - Database::deleteEmails(PlayerManager::getPlayer(sock)->iID, pkt->iEmailIndexArray); - - INITSTRUCT(sP_FE2CL_REP_PC_DELETE_EMAIL_SUCC, resp); - for (int i = 0; i < 5; i++) { - resp.iEmailIndexArray[i] = pkt->iEmailIndexArray[i]; // i'm scared of memcpy - } - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_DELETE_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_DELETE_EMAIL_SUCC)); -} - -void BuddyManager::emailSend(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_SEND_EMAIL)) - return; // malformed packet - - sP_CL2FE_REQ_PC_SEND_EMAIL* pkt = (sP_CL2FE_REQ_PC_SEND_EMAIL*)data->buf; - Player* plr = PlayerManager::getPlayer(sock); - - // sanity checks - bool invalid = false; - int itemCount = 0; - - std::set seen; - for (int i = 0; i < 4; i++) { - int slot = pkt->aItem[i].iSlotNum; - if (slot < 0 || slot >= AINVEN_COUNT) { - invalid = true; - break; - } - - sItemBase *item = &pkt->aItem[i].ItemInven; - sItemBase *real = &plr->Inven[slot]; - - if (item->iID == 0) - continue; - - // was the same item added multiple times? - if (seen.count(slot) > 0) { - invalid = true; - break; - } - seen.insert(slot); - - itemCount++; - if (item->iType != real->iType || item->iID != real->iID - || item->iOpt <= 0 || item->iOpt > real->iOpt) { - invalid = true; - break; - } - } - - if (pkt->iCash < 0 || pkt->iCash > plr->money + 50 + 20 * itemCount || invalid) { - INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, errResp); - errResp.iErrorCode = 1; - errResp.iTo_PCUID = pkt->iTo_PCUID; - sock->sendPacket((void*)&errResp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); - return; - } - - INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_SUCC, resp); - - if (pkt->iCash || pkt->aItem[0].ItemInven.iID) { - // if there are item or taro attachments - Player otherPlr = {}; - Database::getPlayer(&otherPlr, pkt->iTo_PCUID); - if (otherPlr.iID != 0 && plr->PCStyle2.iPayzoneFlag != otherPlr.PCStyle2.iPayzoneFlag) { - // if the players are not in the same time period - INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, resp); - resp.iErrorCode = 9; // error code 9 tells the player they can't send attachments across time - resp.iTo_PCUID = pkt->iTo_PCUID; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); - return; - } - } - - // handle items - std::vector attachments; - std::vector attSlots; - for (int i = 0; i < 4; i++) { - sEmailItemInfoFromCL attachment = pkt->aItem[i]; - - // skip empty slots - if (attachment.ItemInven.iID == 0) - continue; - - resp.aItem[i] = attachment; - attachments.push_back(attachment.ItemInven); - attSlots.push_back(attachment.iSlotNum); - // delete item - plr->Inven[attachment.iSlotNum] = { 0, 0, 0, 0 }; - } - - int cost = pkt->iCash + 50 + 20 * attachments.size(); // attached taros + postage - plr->money -= cost; - Database::EmailData email = { - (int)pkt->iTo_PCUID, // PlayerId - Database::getNextEmailIndex(pkt->iTo_PCUID), // MsgIndex - 0, // ReadFlag (unread) - (pkt->iCash > 0 || attachments.size() > 0) ? 1 : 0, // ItemFlag - plr->iID, // SenderID - U16toU8(plr->PCStyle.szFirstName), // SenderFirstName - U16toU8(plr->PCStyle.szLastName), // SenderLastName - ChatManager::sanitizeText(U16toU8(pkt->szSubject)), // SubjectLine - ChatManager::sanitizeText(U16toU8(pkt->szContent), true), // MsgBody - pkt->iCash, // Taros - (uint64_t)getTimestamp(), // SendTime - 0 // DeleteTime (unimplemented) - }; - - if (!Database::sendEmail(&email, attachments)) { - plr->money += cost; // give money back - // give items back - while (!attachments.empty()) { - sItemBase attachment = attachments.back(); - plr->Inven[attSlots.back()] = attachment; - - attachments.pop_back(); - attSlots.pop_back(); - } - - // send error message - INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, errResp); - errResp.iErrorCode = 1; - errResp.iTo_PCUID = pkt->iTo_PCUID; - sock->sendPacket((void*)&errResp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); - return; - } - - // HACK: use set value packet to force GUI taros update - INITSTRUCT(sP_FE2CL_GM_REP_PC_SET_VALUE, tarosResp); - tarosResp.iPC_ID = plr->iID; - tarosResp.iSetValueType = 5; - tarosResp.iSetValue = plr->money; - sock->sendPacket((void*)&tarosResp, P_FE2CL_GM_REP_PC_SET_VALUE, sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE)); - - resp.iCandy = plr->money; - resp.iTo_PCUID = pkt->iTo_PCUID; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_SEND_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_SUCC)); -} - #pragma region Helper methods int BuddyManager::getAvailableBuddySlot(Player* plr) { diff --git a/src/BuddyManager.hpp b/src/BuddyManager.hpp index 209a8f5..27b3ea2 100644 --- a/src/BuddyManager.hpp +++ b/src/BuddyManager.hpp @@ -33,16 +33,6 @@ namespace BuddyManager { // Buddy warping void reqBuddyWarp(CNSocket* sock, CNPacketData* data); - // Email methods - void emailUpdateCheck(CNSocket* sock, CNPacketData* data); - void emailReceivePageList(CNSocket* sock, CNPacketData* data); - void emailRead(CNSocket* sock, CNPacketData* data); - void emailReceiveTaros(CNSocket* sock, CNPacketData* data); - void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data); - void emailReceiveItemAll(CNSocket* sock, CNPacketData* data); - void emailDelete(CNSocket* sock, CNPacketData* data); - void emailSend(CNSocket* sock, CNPacketData* data); - // helper methods // Name checks diff --git a/src/Email.cpp b/src/Email.cpp new file mode 100644 index 0000000..00230a1 --- /dev/null +++ b/src/Email.cpp @@ -0,0 +1,332 @@ +#include "Email.hpp" + +// New email notification +void Email::emailUpdateCheck(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK)) + return; // malformed packet + + INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp); + resp.iNewEmailCnt = Database::getUnreadEmailCount(PlayerManager::getPlayer(sock)->iID); + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_NEW_EMAIL, sizeof(sP_FE2CL_REP_PC_NEW_EMAIL)); +} + +// Retrieve page of emails +void Email::emailReceivePageList(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST)) + return; // malformed packet + + sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST*)data->buf; + + INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC, resp); + resp.iPageNum = pkt->iPageNum; + + std::vector emails = Database::getEmails(PlayerManager::getPlayer(sock)->iID, pkt->iPageNum); + for (int i = 0; i < emails.size(); i++) { + // convert each email and load them into the packet + Database::EmailData* email = &emails.at(i); + sEmailInfo* emailInfo = new sEmailInfo(); + emailInfo->iEmailIndex = email->MsgIndex; + emailInfo->iReadFlag = email->ReadFlag; + emailInfo->iItemCandyFlag = email->ItemFlag; + emailInfo->iFromPCUID = email->SenderId; + emailInfo->SendTime = timeStampToStruct(email->SendTime); + emailInfo->DeleteTime = timeStampToStruct(email->DeleteTime); + U8toU16(email->SenderFirstName, emailInfo->szFirstName, sizeof(emailInfo->szFirstName)); + U8toU16(email->SenderLastName, emailInfo->szLastName, sizeof(emailInfo->szLastName)); + U8toU16(email->SubjectLine, emailInfo->szSubject, sizeof(emailInfo->szSubject)); + resp.aEmailInfo[i] = *emailInfo; + } + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_PAGE_LIST_SUCC)); +} + +// Read individual email +void Email::emailRead(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_READ_EMAIL)) + return; // malformed packet + + sP_CL2FE_REQ_PC_READ_EMAIL* pkt = (sP_CL2FE_REQ_PC_READ_EMAIL*)data->buf; + + Player* plr = PlayerManager::getPlayer(sock); + + Database::EmailData email = Database::getEmail(plr->iID, pkt->iEmailIndex); + sItemBase* attachments = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); + email.ReadFlag = 1; // mark as read + Database::updateEmailContent(&email); + + INITSTRUCT(sP_FE2CL_REP_PC_READ_EMAIL_SUCC, resp); + resp.iEmailIndex = pkt->iEmailIndex; + resp.iCash = email.Taros; + for (int i = 0; i < 4; i++) { + resp.aItem[i] = attachments[i]; + } + U8toU16(email.MsgBody, (char16_t*)resp.szContent, sizeof(resp.szContent)); + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_READ_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_READ_EMAIL_SUCC)); +} + +// Retrieve attached taros from email +void Email::emailReceiveTaros(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY)) + return; // malformed packet + + sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_CANDY*)data->buf; + + Player* plr = PlayerManager::getPlayer(sock); + + Database::EmailData email = Database::getEmail(plr->iID, pkt->iEmailIndex); + // money transfer + plr->money += email.Taros; + email.Taros = 0; + // update Taros in email + Database::updateEmailContent(&email); + + INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC, resp); + resp.iCandy = plr->money; + resp.iEmailIndex = pkt->iEmailIndex; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_CANDY_SUCC)); +} + +// Retrieve individual attached item from email +void Email::emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM)) + return; // malformed packet + + sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iSlotNum < 1 || pkt->iSlotNum > 4) + return; // sanity check + + // get email item from db and delete it + sItemBase* attachments = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); + sItemBase itemFrom = attachments[pkt->iEmailItemSlot - 1]; + Database::deleteEmailAttachments(plr->iID, pkt->iEmailIndex, pkt->iEmailItemSlot); + + // move item to player inventory + sItemBase& itemTo = plr->Inven[pkt->iSlotNum]; + itemTo.iID = itemFrom.iID; + itemTo.iOpt = itemFrom.iOpt; + itemTo.iTimeLimit = itemFrom.iTimeLimit; + itemTo.iType = itemFrom.iType; + + INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC, resp); + resp.iEmailIndex = pkt->iEmailIndex; + resp.iEmailItemSlot = pkt->iEmailItemSlot; + resp.iSlotNum = pkt->iSlotNum; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_SUCC)); + + // update inventory + INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp2); + resp2.eIL = 1; + resp2.iSlotNum = resp.iSlotNum; + resp2.Item = itemTo; + + sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC)); +} + +// Retrieve all attached items from email +void Email::emailReceiveItemAll(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL)) + return; // malformed packet + + sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL* pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL*)data->buf; + + // move items to player inventory + Player* plr = PlayerManager::getPlayer(sock); + sItemBase* itemsFrom = Database::getEmailAttachments(plr->iID, pkt->iEmailIndex); + for (int i = 0; i < 4; i++) { + int slot = ItemManager::findFreeSlot(plr); + if (slot < 0 || slot >= AINVEN_COUNT) { + INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_FAIL, failResp); + failResp.iEmailIndex = pkt->iEmailIndex; + failResp.iErrorCode = 0; // ??? + break; // sanity check; should never happen + } + + // copy data over + sItemBase itemFrom = itemsFrom[i]; + sItemBase& itemTo = plr->Inven[slot]; + itemTo.iID = itemFrom.iID; + itemTo.iOpt = itemFrom.iOpt; + itemTo.iTimeLimit = itemFrom.iTimeLimit; + itemTo.iType = itemFrom.iType; + + // update inventory + INITSTRUCT(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC, resp2); + resp2.eIL = 1; + resp2.iSlotNum = slot; + resp2.Item = itemTo; + + sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC)); + } + + // delete all items from db + Database::deleteEmailAttachments(plr->iID, pkt->iEmailIndex, -1); + + INITSTRUCT(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC, resp); + resp.iEmailIndex = pkt->iEmailIndex; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC, sizeof(sP_FE2CL_REP_PC_RECV_EMAIL_ITEM_ALL_SUCC)); +} + +// Delete an email +void Email::emailDelete(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_DELETE_EMAIL)) + return; // malformed packet + + sP_CL2FE_REQ_PC_DELETE_EMAIL* pkt = (sP_CL2FE_REQ_PC_DELETE_EMAIL*)data->buf; + + Database::deleteEmails(PlayerManager::getPlayer(sock)->iID, pkt->iEmailIndexArray); + + INITSTRUCT(sP_FE2CL_REP_PC_DELETE_EMAIL_SUCC, resp); + for (int i = 0; i < 5; i++) { + resp.iEmailIndexArray[i] = pkt->iEmailIndexArray[i]; // i'm scared of memcpy + } + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_DELETE_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_DELETE_EMAIL_SUCC)); +} + +// Send an email +void Email::emailSend(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_SEND_EMAIL)) + return; // malformed packet + + sP_CL2FE_REQ_PC_SEND_EMAIL* pkt = (sP_CL2FE_REQ_PC_SEND_EMAIL*)data->buf; + Player* plr = PlayerManager::getPlayer(sock); + + // sanity checks + bool invalid = false; + int itemCount = 0; + + std::set seen; + for (int i = 0; i < 4; i++) { + int slot = pkt->aItem[i].iSlotNum; + if (slot < 0 || slot >= AINVEN_COUNT) { + invalid = true; + break; + } + + sItemBase* item = &pkt->aItem[i].ItemInven; + sItemBase* real = &plr->Inven[slot]; + + if (item->iID == 0) + continue; + + // was the same item added multiple times? + if (seen.count(slot) > 0) { + invalid = true; + break; + } + seen.insert(slot); + + itemCount++; + if (item->iType != real->iType || item->iID != real->iID + || item->iOpt <= 0 || item->iOpt > real->iOpt) { + invalid = true; + break; + } + } + + if (pkt->iCash < 0 || pkt->iCash > plr->money + 50 + 20 * itemCount || invalid) { + INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, errResp); + errResp.iErrorCode = 1; + errResp.iTo_PCUID = pkt->iTo_PCUID; + sock->sendPacket((void*)&errResp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); + return; + } + + INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_SUCC, resp); + + if (pkt->iCash || pkt->aItem[0].ItemInven.iID) { + // if there are item or taro attachments + Player otherPlr = {}; + Database::getPlayer(&otherPlr, pkt->iTo_PCUID); + if (otherPlr.iID != 0 && plr->PCStyle2.iPayzoneFlag != otherPlr.PCStyle2.iPayzoneFlag) { + // if the players are not in the same time period + INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, resp); + resp.iErrorCode = 9; // error code 9 tells the player they can't send attachments across time + resp.iTo_PCUID = pkt->iTo_PCUID; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); + return; + } + } + + // handle items + std::vector attachments; + std::vector attSlots; + for (int i = 0; i < 4; i++) { + sEmailItemInfoFromCL attachment = pkt->aItem[i]; + + // skip empty slots + if (attachment.ItemInven.iID == 0) + continue; + + resp.aItem[i] = attachment; + attachments.push_back(attachment.ItemInven); + attSlots.push_back(attachment.iSlotNum); + // delete item + plr->Inven[attachment.iSlotNum] = { 0, 0, 0, 0 }; + } + + int cost = pkt->iCash + 50 + 20 * attachments.size(); // attached taros + postage + plr->money -= cost; + Database::EmailData email = { + (int)pkt->iTo_PCUID, // PlayerId + Database::getNextEmailIndex(pkt->iTo_PCUID), // MsgIndex + 0, // ReadFlag (unread) + (pkt->iCash > 0 || attachments.size() > 0) ? 1 : 0, // ItemFlag + plr->iID, // SenderID + U16toU8(plr->PCStyle.szFirstName), // SenderFirstName + U16toU8(plr->PCStyle.szLastName), // SenderLastName + ChatManager::sanitizeText(U16toU8(pkt->szSubject)), // SubjectLine + ChatManager::sanitizeText(U16toU8(pkt->szContent), true), // MsgBody + pkt->iCash, // Taros + (uint64_t)getTimestamp(), // SendTime + 0 // DeleteTime (unimplemented) + }; + + if (!Database::sendEmail(&email, attachments)) { + plr->money += cost; // give money back + // give items back + while (!attachments.empty()) { + sItemBase attachment = attachments.back(); + plr->Inven[attSlots.back()] = attachment; + + attachments.pop_back(); + attSlots.pop_back(); + } + + // send error message + INITSTRUCT(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL, errResp); + errResp.iErrorCode = 1; + errResp.iTo_PCUID = pkt->iTo_PCUID; + sock->sendPacket((void*)&errResp, P_FE2CL_REP_PC_SEND_EMAIL_FAIL, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_FAIL)); + return; + } + + // HACK: use set value packet to force GUI taros update + INITSTRUCT(sP_FE2CL_GM_REP_PC_SET_VALUE, tarosResp); + tarosResp.iPC_ID = plr->iID; + tarosResp.iSetValueType = 5; + tarosResp.iSetValue = plr->money; + sock->sendPacket((void*)&tarosResp, P_FE2CL_GM_REP_PC_SET_VALUE, sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE)); + + resp.iCandy = plr->money; + resp.iTo_PCUID = pkt->iTo_PCUID; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_SEND_EMAIL_SUCC, sizeof(sP_FE2CL_REP_PC_SEND_EMAIL_SUCC)); +} + +void Email::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK, emailUpdateCheck); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_PAGE_LIST, emailReceivePageList); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_READ_EMAIL, emailRead); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_CANDY, emailReceiveTaros); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM, emailReceiveItemSingle); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_RECV_EMAIL_ITEM_ALL, emailReceiveItemAll); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_DELETE_EMAIL, emailDelete); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SEND_EMAIL, emailSend); +} diff --git a/src/Email.hpp b/src/Email.hpp new file mode 100644 index 0000000..d72e262 --- /dev/null +++ b/src/Email.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "CNProtocol.hpp" +#include "CNStructs.hpp" +#include "CNShardServer.hpp" + +#include "Database.hpp" +#include "PlayerManager.hpp" +#include "ItemManager.hpp" +#include "ChatManager.hpp" + +namespace Email { + void init(); + + void emailUpdateCheck(CNSocket* sock, CNPacketData* data); + void emailReceivePageList(CNSocket* sock, CNPacketData* data); + void emailRead(CNSocket* sock, CNPacketData* data); + void emailReceiveTaros(CNSocket* sock, CNPacketData* data); + void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data); + void emailReceiveItemAll(CNSocket* sock, CNPacketData* data); + void emailDelete(CNSocket* sock, CNPacketData* data); + void emailSend(CNSocket* sock, CNPacketData* data); +} diff --git a/src/main.cpp b/src/main.cpp index 34042f0..50e2ddd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include "Monitor.hpp" #include "RacingManager.hpp" #include "Trading.hpp" +#include "Email.hpp" #include "settings.hpp" @@ -110,6 +111,7 @@ int main() { NPCManager::init(); TransportManager::init(); BuddyManager::init(); + Email::init(); GroupManager::init(); RacingManager::init(); Database::open();