From e92a5a2f8bd4d10baf1cb474a1e072d9b016c76b Mon Sep 17 00:00:00 2001 From: CPunch Date: Fri, 12 Mar 2021 20:09:36 -0600 Subject: [PATCH] [refactor] Split ItemManager.cpp into Vendor.cpp & Trading.cpp - added sources to Makefile - Added Trading::init() to main.cpp --- Makefile | 4 + src/ItemManager.cpp | 606 +++++++------------------------------------- src/ItemManager.hpp | 21 -- src/NPCManager.cpp | 5 +- src/TableData.cpp | 5 +- src/Trading.cpp | 432 +++++++++++++++++++++++++++++++ src/Trading.hpp | 7 + src/Vendor.cpp | 3 + src/Vendor.hpp | 11 + src/main.cpp | 2 + 10 files changed, 553 insertions(+), 543 deletions(-) create mode 100644 src/Trading.cpp create mode 100644 src/Trading.hpp create mode 100644 src/Vendor.cpp create mode 100644 src/Vendor.hpp diff --git a/Makefile b/Makefile index e08753c..bc94891 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,8 @@ CXXSRC=\ src/GroupManager.cpp\ src/Monitor.cpp\ src/RacingManager.cpp\ + src/Vendor.cpp\ + src/Trading.cpp\ # headers (for timestamp purposes) CHDR=\ @@ -95,6 +97,8 @@ CXXHDR=\ src/GroupManager.hpp\ src/Monitor.hpp\ src/RacingManager.hpp\ + src/Vendor.hpp\ + src/Trading.hpp\ COBJ=$(CSRC:.c=.o) CXXOBJ=$(CXXSRC:.cpp=.o) diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index 516f4b7..240d1e9 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -10,7 +10,6 @@ #include std::map, ItemManager::Item> ItemManager::ItemData; -std::map> ItemManager::VendorTables; std::map ItemManager::CrocPotTable; std::map> ItemManager::RarityRatios; std::map ItemManager::Crates; @@ -20,28 +19,62 @@ std::map>> ItemManager::Cod #ifdef ACADEMY std::map ItemManager::NanoCapsules; // crate id -> nano id + +void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) { + Player* plr = PlayerManager::getPlayer(sock); + int32_t nanoId = ItemManager::NanoCapsules[chest->iID]; + + // chest opening acknowledgement packet + INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); + resp.iSlotNum = slot; + + // in order to remove capsule form inventory, we have to send item reward packet with empty item + const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + + // we know it's only one trailing struct, so we can skip full validation + uint8_t respbuf[resplen]; // not a variable length array, don't worry + sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; + sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); + + // don't forget to zero the buffer! + memset(respbuf, 0, resplen); + + // maintain stats + reward->m_iCandy = plr->money; + reward->m_iFusionMatter = plr->fusionmatter; + reward->iFatigue = 100; // prevents warning message + reward->iFatigue_Level = 1; + reward->iItemCnt = 1; // remember to update resplen if you change this + reward->m_iBatteryN = plr->batteryN; + reward->m_iBatteryW = plr->batteryW; + + item->iSlotNum = slot; + item->eIL = 1; + + // update player serverside + plr->Inven[slot] = item->sItem; + + // transmit item + sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); + + // transmit chest opening acknowledgement packet + sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC)); + + // check if player doesn't already have this nano + if (plr->Nanos[nanoId].iID != 0) { + INITSTRUCT(sP_FE2CL_GM_REP_PC_ANNOUNCE, msg); + msg.iDuringTime = 4; + std::string text = "You have already aquired this nano!"; + U8toU16(text, msg.szAnnounceMsg, sizeof(text)); + sock->sendPacket((void*)&msg, P_FE2CL_GM_REP_PC_ANNOUNCE, sizeof(sP_FE2CL_GM_REP_PC_ANNOUNCE)); + return; + } + NanoManager::addNano(sock, nanoId, -1, false); +} #endif -void ItemManager::init() { - REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler); - // this one is for gumballs - REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_USE, itemUseHandler); - // Bank - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler); - // Trade handlers - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, tradeOffer); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, tradeOfferAccept); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, tradeOfferRefusal); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, tradeConfirm); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, tradeConfirmCancel); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, tradeRegisterItem); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, tradeUnregisterItem); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, tradeRegisterCash); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler); -} - -void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { +void itemMoveHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_MOVE)) return; // ignore the malformed packet @@ -62,20 +95,20 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { // get the fromItem sItemBase *fromItem; - switch ((SlotType)itemmove->eFrom) { - case SlotType::EQUIP: + switch ((ItemManager::SlotType)itemmove->eFrom) { + case ItemManager::SlotType::EQUIP: if (itemmove->iFromSlotNum >= AEQUIP_COUNT) return; fromItem = &plr->Equip[itemmove->iFromSlotNum]; break; - case SlotType::INVENTORY: + case ItemManager::SlotType::INVENTORY: if (itemmove->iFromSlotNum >= AINVEN_COUNT) return; fromItem = &plr->Inven[itemmove->iFromSlotNum]; break; - case SlotType::BANK: + case ItemManager::SlotType::BANK: if (itemmove->iFromSlotNum >= ABANK_COUNT) return; @@ -88,20 +121,20 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { // get the toItem sItemBase* toItem; - switch ((SlotType)itemmove->eTo) { - case SlotType::EQUIP: + switch ((ItemManager::SlotType)itemmove->eTo) { + case ItemManager::SlotType::EQUIP: if (itemmove->iToSlotNum >= AEQUIP_COUNT) return; toItem = &plr->Equip[itemmove->iToSlotNum]; break; - case SlotType::INVENTORY: + case ItemManager::SlotType::INVENTORY: if (itemmove->iToSlotNum >= AINVEN_COUNT) return; toItem = &plr->Inven[itemmove->iToSlotNum]; break; - case SlotType::BANK: + case ItemManager::SlotType::BANK: if (itemmove->iToSlotNum >= ABANK_COUNT) return; @@ -113,7 +146,7 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { } // if equipping an item, validate that it's of the correct type for the slot - if ((SlotType)itemmove->eTo == SlotType::EQUIP) { + if ((ItemManager::SlotType)itemmove->eTo == ItemManager::SlotType::EQUIP) { if (fromItem->iType == 10 && itemmove->iToSlotNum != 8) return; // vehicle in wrong slot else if (fromItem->iType != 10 @@ -131,8 +164,8 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { resp.FromSlotItem = *fromItem; // swap/stack items in session - Item* itemDat = getItemData(toItem->iID, toItem->iType); - Item* itemDatFrom = getItemData(fromItem->iID, fromItem->iType); + ItemManager::Item* itemDat = ItemManager::getItemData(toItem->iID, toItem->iType); + ItemManager::Item* itemDatFrom = ItemManager::getItemData(fromItem->iID, fromItem->iType); if (itemDat != nullptr && itemDatFrom != nullptr && itemDat->stackSize > 1 && itemDat == itemDatFrom && fromItem->iOpt < itemDat->stackSize && toItem->iOpt < itemDat->stackSize) { // items are stackable, identical, and not maxed, so run stacking logic @@ -165,11 +198,11 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { } // send equip change to viewable players - if (itemmove->eFrom == (int)SlotType::EQUIP || itemmove->eTo == (int)SlotType::EQUIP) { + if (itemmove->eFrom == (int)ItemManager::SlotType::EQUIP || itemmove->eTo == (int)ItemManager::SlotType::EQUIP) { INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, equipChange); equipChange.iPC_ID = plr->iID; - if (itemmove->eTo == (int)SlotType::EQUIP) { + if (itemmove->eTo == (int)ItemManager::SlotType::EQUIP) { equipChange.iEquipSlotNum = itemmove->iToSlotNum; equipChange.EquipSlotItem = resp.FromSlotItem; } else { @@ -185,14 +218,14 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { PlayerManager::sendToViewable(sock, (void*)&equipChange, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE)); // set equipment stats serverside - setItemStats(plr); + ItemManager::setItemStats(plr); } // send response sock->sendPacket((void*)&resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC)); } -void ItemManager::itemDeleteHandler(CNSocket* sock, CNPacketData* data) { +void itemDeleteHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_DELETE)) return; // ignore the malformed packet @@ -212,7 +245,7 @@ void ItemManager::itemDeleteHandler(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_ITEM_DELETE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC)); } -void ItemManager::itemUseHandler(CNSocket* sock, CNPacketData* data) { +void itemUseHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_USE)) return; // ignore the malformed packet sP_CL2FE_REQ_ITEM_USE* request = (sP_CL2FE_REQ_ITEM_USE*)data->buf; @@ -295,7 +328,7 @@ void ItemManager::itemUseHandler(CNSocket* sock, CNPacketData* data) { NPCManager::EggBuffs[key] = until; } -void ItemManager::itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { +void itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_BANK_OPEN)) return; // ignore the malformed packet @@ -310,425 +343,7 @@ void ItemManager::itemBankOpenHandler(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_BANK_OPEN_SUCC, sizeof(sP_FE2CL_REP_PC_BANK_OPEN_SUCC)); } -void ItemManager::tradeOffer(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_OFFER* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER*)data->buf; - - CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(otherSock); - - if (plr->isTrading) { - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); - resp.iID_Request = pacdat->iID_To; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); - return; // prevent trading with a player already trading - } - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER)); -} - -void ItemManager::tradeOfferAccept(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT*)data->buf; - - CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(sock); - Player* plr2 = PlayerManager::getPlayer(otherSock); - - if (plr2->isTrading) { - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); - resp.iID_Request = pacdat->iID_From; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); - return; // prevent trading with a player already trading - } - - // clearing up trade slots - plr->moneyInTrade = 0; - plr2->moneyInTrade = 0; - memset(&plr->Trade, 0, sizeof(plr->Trade)); - memset(&plr2->Trade, 0, sizeof(plr2->Trade)); - - // marking players as traders - plr->isTrading = true; - plr2->isTrading = true; - - // marking players as unconfirmed - plr->isTradeConfirm = false; - plr2->isTradeConfirm = false; - - // inform the other player that offer is accepted - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC)); -} - -void ItemManager::tradeOfferRefusal(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL*)data->buf; - - CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); -} - -void ItemManager::tradeConfirm(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_CONFIRM* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM*)data->buf; - - CNSocket* otherSock; // weird flip flop because we need to know who the other player is - if (pacdat->iID_Request == pacdat->iID_From) - otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - else - otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(sock); - Player* plr2 = PlayerManager::getPlayer(otherSock); - - if (!(plr->isTrading && plr2->isTrading)) { // both players must be trading - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp); - resp.iID_Request = plr2->iID; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - resp.iID_Request = plr->iID; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - return; - } - - // send the confirm packet - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); - - if (!(plr2->isTradeConfirm)) { - plr->isTradeConfirm = true; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); - return; - } - - // both players are no longer trading - plr->isTrading = false; - plr2->isTrading = false; - plr->isTradeConfirm = false; - plr2->isTradeConfirm = false; - - if (doTrade(plr, plr2)) { // returns false if not enough slots - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, resp2); - resp2.iID_Request = pacdat->iID_Request; - resp2.iID_From = pacdat->iID_From; - resp2.iID_To = pacdat->iID_To; - plr->money = plr->money + plr2->moneyInTrade - plr->moneyInTrade; - resp2.iCandy = plr->money; - memcpy(resp2.Item, plr2->Trade, sizeof(plr2->Trade)); - memcpy(resp2.ItemStay, plr->Trade, sizeof(plr->Trade)); - - sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC)); - - plr2->money = plr2->money + plr->moneyInTrade - plr2->moneyInTrade; - resp2.iCandy = plr2->money; - memcpy(resp2.Item, plr->Trade, sizeof(plr->Trade)); - memcpy(resp2.ItemStay, plr2->Trade, sizeof(plr2->Trade)); - - otherSock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC)); - } else { - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp); - resp.iID_Request = plr2->iID; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - resp.iID_Request = plr->iID; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - return; - } -} - -bool ItemManager::doTrade(Player* plr, Player* plr2) { - // init dummy inventories - sItemBase plrInven[AINVEN_COUNT]; - sItemBase plr2Inven[AINVEN_COUNT]; - memcpy(plrInven, plr->Inven, AINVEN_COUNT * sizeof(sItemBase)); - memcpy(plr2Inven, plr2->Inven, AINVEN_COUNT * sizeof(sItemBase)); - - for (int i = 0; i < 5; i++) { - // remove items offered by us - if (plr->Trade[i].iID != 0) { - if (plrInven[plr->Trade[i].iInvenNum].iID == 0 - || plr->Trade[i].iID != plrInven[plr->Trade[i].iInvenNum].iID - || plr->Trade[i].iType != plrInven[plr->Trade[i].iInvenNum].iType) // pulling a fast one on us - return false; - - if (plr->Trade[i].iOpt < 1) { - std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl; - plr->Trade[i].iOpt = 1; - } - - // for stacked items - plrInven[plr->Trade[i].iInvenNum].iOpt -= plr->Trade[i].iOpt; - - if (plrInven[plr->Trade[i].iInvenNum].iOpt == 0) { - plrInven[plr->Trade[i].iInvenNum].iID = 0; - plrInven[plr->Trade[i].iInvenNum].iType = 0; - plrInven[plr->Trade[i].iInvenNum].iOpt = 0; - } else if (plrInven[plr->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt - return false; - } - } - - if (plr2->Trade[i].iID != 0) { - if (plr2Inven[plr2->Trade[i].iInvenNum].iID == 0 - || plr2->Trade[i].iID != plr2Inven[plr2->Trade[i].iInvenNum].iID - || plr2->Trade[i].iType != plr2Inven[plr2->Trade[i].iInvenNum].iType) // pulling a fast one on us - return false; - - if (plr2->Trade[i].iOpt < 1) { - std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl; - plr2->Trade[i].iOpt = 1; - } - - // for stacked items - plr2Inven[plr2->Trade[i].iInvenNum].iOpt -= plr2->Trade[i].iOpt; - - if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt == 0) { - plr2Inven[plr2->Trade[i].iInvenNum].iID = 0; - plr2Inven[plr2->Trade[i].iInvenNum].iType = 0; - plr2Inven[plr2->Trade[i].iInvenNum].iOpt = 0; - } else if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt - return false; - } - } - - // add items offered to us - if (plr2->Trade[i].iID != 0) { - for (int n = 0; n < AINVEN_COUNT; n++) { - if (plrInven[n].iID == 0) { - plrInven[n].iID = plr2->Trade[i].iID; - plrInven[n].iType = plr2->Trade[i].iType; - plrInven[n].iOpt = plr2->Trade[i].iOpt; - plr2->Trade[i].iInvenNum = n; - break; - } - - if (n >= AINVEN_COUNT - 1) - return false; // not enough space - } - } - - if (plr->Trade[i].iID != 0) { - for (int n = 0; n < AINVEN_COUNT; n++) { - if (plr2Inven[n].iID == 0) { - plr2Inven[n].iID = plr->Trade[i].iID; - plr2Inven[n].iType = plr->Trade[i].iType; - plr2Inven[n].iOpt = plr->Trade[i].iOpt; - plr->Trade[i].iInvenNum = n; - break; - } - - if (n >= AINVEN_COUNT - 1) - return false; // not enough space - } - } - } - - // if everything went well, back into player inventory it goes - memcpy(plr->Inven, plrInven, AINVEN_COUNT * sizeof(sItemBase)); - memcpy(plr2->Inven, plr2Inven, AINVEN_COUNT * sizeof(sItemBase)); - - return true; -} - -void ItemManager::tradeConfirmCancel(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL*)data->buf; - - CNSocket* otherSock; // weird flip flop because we need to know who the other player is - if (pacdat->iID_Request == pacdat->iID_From) - otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - else - otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(sock); - Player* plr2 = PlayerManager::getPlayer(otherSock); - - // both players are not trading nor are in a confirmed state - plr->isTrading = false; - plr->isTradeConfirm = false; - plr2->isTrading = false; - plr2->isTradeConfirm = false; - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL)); -} - -void ItemManager::tradeRegisterItem(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER*)data->buf; - - if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4) - return; // sanity check, there are only 5 trade slots - - CNSocket* otherSock; // weird flip flop because we need to know who the other player is - if (pacdat->iID_Request == pacdat->iID_From) - otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - else - otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(sock); - plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item; - plr->isTradeConfirm = false; - - // since you can spread items like gumballs over multiple slots, we need to count them all - // to make sure the inventory shows the right value during trade. - int count = 0; - for (int i = 0; i < 5; i++) { - if (plr->Trade[i].iInvenNum == pacdat->Item.iInvenNum) - count += plr->Trade[i].iOpt; - } - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - resp.TradeItem = pacdat->Item; - resp.InvenItem = pacdat->Item; - resp.InvenItem.iOpt = plr->Inven[pacdat->Item.iInvenNum].iOpt - count; // subtract this count - - if (resp.InvenItem.iOpt < 0) // negative count items, doTrade() will block this later on - std::cout << "[WARN] tradeRegisterItem: an item went negative count client side." << std::endl; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC)); -} - -void ItemManager::tradeUnregisterItem(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER*)data->buf; - - if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4) - return; // sanity check, there are only 5 trade slots - - CNSocket* otherSock; // weird flip flop because we need to know who the other player is - if (pacdat->iID_Request == pacdat->iID_From) - otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - else - otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - Player* plr = PlayerManager::getPlayer(sock); - plr->isTradeConfirm = false; - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - resp.TradeItem = pacdat->Item; - resp.InvenItem = plr->Trade[pacdat->Item.iSlotNum]; - - memset(&plr->Trade[pacdat->Item.iSlotNum], 0, sizeof(plr->Trade[pacdat->Item.iSlotNum])); // clean up item slot - - // since you can spread items like gumballs over multiple slots, we need to count them all - // to make sure the inventory shows the right value during trade. - int count = 0; - for (int i = 0; i < 5; i++) { - if (plr->Trade[i].iInvenNum == resp.InvenItem.iInvenNum) - count += plr->Trade[i].iOpt; - } - - resp.InvenItem.iOpt = plr->Inven[resp.InvenItem.iInvenNum].iOpt - count; // subtract this count - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC)); -} - -void ItemManager::tradeRegisterCash(CNSocket* sock, CNPacketData* data) { - if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER)) - return; // ignore the malformed packet - - sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER*)data->buf; - - Player* plr = PlayerManager::getPlayer(sock); - - if (pacdat->iCandy < 0 || pacdat->iCandy > plr->money) - return; // famous glitch, begone - - CNSocket* otherSock; // weird flip flop because we need to know who the other player is - if (pacdat->iID_Request == pacdat->iID_From) - otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - else - otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - - if (otherSock == nullptr) - return; - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - resp.iCandy = pacdat->iCandy; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC)); - - plr->moneyInTrade = pacdat->iCandy; - plr->isTradeConfirm = false; -} - -void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) { +void chestOpenHandler(CNSocket *sock, CNPacketData *data) { if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN)) return; // ignore the malformed packet @@ -750,7 +365,7 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) { #ifdef ACADEMY // check if chest isn't a nano capsule - if (NanoCapsules.find(chest->iID) != NanoCapsules.end()) + if (ItemManager::NanoCapsules.find(chest->iID) != ItemManager::NanoCapsules.end()) return nanoCapsuleHandler(sock, pkt->iSlotNum, chest); #endif @@ -786,24 +401,24 @@ void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) { bool failing = false; // find the crate - if (Crates.find(chest->iID) == Crates.end()) { + if (ItemManager::Crates.find(chest->iID) == ItemManager::Crates.end()) { std::cout << "[WARN] Crate " << chest->iID << " not found!" << std::endl; failing = true; } - Crate& crate = Crates[chest->iID]; + Crate& crate = ItemManager::Crates[chest->iID]; if (!failing) - itemSetId = getItemSetId(crate, chest->iID); + itemSetId = ItemManager::getItemSetId(crate, chest->iID); if (itemSetId == -1) failing = true; if (!failing) - rarity = getRarity(crate, itemSetId); + rarity = ItemManager::getRarity(crate, itemSetId); if (rarity == -1) failing = true; if (!failing) - ret = getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); + ret = ItemManager::getCrateItem(item->sItem, itemSetId, rarity, plr->PCStyle.iGender); if (ret == -1) failing = true; @@ -879,7 +494,7 @@ int ItemManager::getRarity(Crate& crate, int itemSetId) { } int ItemManager::getCrateItem(sItemBase& result, int itemSetId, int rarity, int playerGender) { - std::pair key = std::make_pair(itemSetId, rarity); + auto key = std::make_pair(itemSetId, rarity); if (CrateItems.find(key) == CrateItems.end()) { std::cout << "[WARN] Item Set ID " << itemSetId << " Rarity " << rarity << " does not exist" << std::endl; @@ -998,57 +613,12 @@ void ItemManager::updateEquips(CNSocket* sock, Player* plr) { } } -#ifdef ACADEMY -void ItemManager::nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest) { - Player* plr = PlayerManager::getPlayer(sock); - int32_t nanoId = NanoCapsules[chest->iID]; - - // chest opening acknowledgement packet - INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp); - resp.iSlotNum = slot; - - // in order to remove capsule form inventory, we have to send item reward packet with empty item - const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward); - assert(resplen < CN_PACKET_BUFFER_SIZE - 8); - - // we know it's only one trailing struct, so we can skip full validation - uint8_t respbuf[resplen]; // not a variable length array, don't worry - sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf; - sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM)); - - // don't forget to zero the buffer! - memset(respbuf, 0, resplen); - - // maintain stats - reward->m_iCandy = plr->money; - reward->m_iFusionMatter = plr->fusionmatter; - reward->iFatigue = 100; // prevents warning message - reward->iFatigue_Level = 1; - reward->iItemCnt = 1; // remember to update resplen if you change this - reward->m_iBatteryN = plr->batteryN; - reward->m_iBatteryW = plr->batteryW; - - item->iSlotNum = slot; - item->eIL = 1; - - // update player serverside - plr->Inven[slot] = item->sItem; - - // transmit item - sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen); - - // transmit chest opening acknowledgement packet - sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC)); - - // check if player doesn't already have this nano - if (plr->Nanos[nanoId].iID != 0) { - INITSTRUCT(sP_FE2CL_GM_REP_PC_ANNOUNCE, msg); - msg.iDuringTime = 4; - std::string text = "You have already aquired this nano!"; - U8toU16(text, msg.szAnnounceMsg, sizeof(text)); - sock->sendPacket((void*)&msg, P_FE2CL_GM_REP_PC_ANNOUNCE, sizeof(sP_FE2CL_GM_REP_PC_ANNOUNCE)); - return; - } - NanoManager::addNano(sock, nanoId, -1, false); -} -#endif +void ItemManager::init() { + REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler); + // this one is for gumballs + REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_USE, itemUseHandler); + // Bank + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler); +} \ No newline at end of file diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index a3b517f..a300b48 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -3,9 +3,6 @@ #include "CNShardServer.hpp" #include "Player.hpp" -struct VendorListing { - int sort, type, iID; -}; struct CrocPotEntry { int multStats, multLooks; float base, rd0, rd1, rd2, rd3; @@ -31,7 +28,6 @@ namespace ItemManager { }; // hopefully this is fine since it's never modified after load extern std::map, Item> ItemData; // -> data - extern std::map> VendorTables; extern std::map CrocPotTable; // level gap -> entry extern std::map> RarityRatios; extern std::map Crates; @@ -42,22 +38,6 @@ namespace ItemManager { void init(); - void itemMoveHandler(CNSocket* sock, CNPacketData* data); - void itemDeleteHandler(CNSocket* sock, CNPacketData* data); - void itemUseHandler(CNSocket* sock, CNPacketData* data); - // Bank - void itemBankOpenHandler(CNSocket* sock, CNPacketData* data); - void tradeOffer(CNSocket* sock, CNPacketData* data); - void tradeOfferAccept(CNSocket* sock, CNPacketData* data); - void tradeOfferRefusal(CNSocket* sock, CNPacketData* data); - void tradeConfirm(CNSocket* sock, CNPacketData* data); - bool doTrade(Player* plr, Player* plr2); - void tradeConfirmCancel(CNSocket* sock, CNPacketData* data); - void tradeRegisterItem(CNSocket* sock, CNPacketData* data); - void tradeUnregisterItem(CNSocket* sock, CNPacketData* data); - void tradeRegisterCash(CNSocket* sock, CNPacketData* data); - void chestOpenHandler(CNSocket* sock, CNPacketData* data); - // crate opening logic with all helper functions int getItemSetId(Crate& crate, int crateId); int getRarity(Crate& crate, int itemSetId); @@ -71,6 +51,5 @@ namespace ItemManager { #ifdef ACADEMY extern std::map NanoCapsules; // crate id -> nano id - void nanoCapsuleHandler(CNSocket* sock, int slot, sItemBase *chest); #endif } diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index dd6407b..91e86b8 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -8,6 +8,7 @@ #include "TableData.hpp" #include "GroupManager.hpp" #include "RacingManager.hpp" +#include "Vendor.hpp" #include #include @@ -318,10 +319,10 @@ void NPCManager::npcVendorTable(CNSocket* sock, CNPacketData* data) { sP_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE* req = (sP_CL2FE_REQ_PC_VENDOR_TABLE_UPDATE*)data->buf; - if (req->iVendorID != req->iNPC_ID || ItemManager::VendorTables.find(req->iVendorID) == ItemManager::VendorTables.end()) + if (req->iVendorID != req->iNPC_ID || Vendor::VendorTables.find(req->iVendorID) == Vendor::VendorTables.end()) return; - std::vector listings = ItemManager::VendorTables[req->iVendorID]; + std::vector listings = Vendor::VendorTables[req->iVendorID]; INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC, resp); diff --git a/src/TableData.cpp b/src/TableData.cpp index b05f689..d1da352 100644 --- a/src/TableData.cpp +++ b/src/TableData.cpp @@ -8,6 +8,7 @@ #include "ChunkManager.hpp" #include "NanoManager.hpp" #include "RacingManager.hpp" +#include "Vendor.hpp" #include "JSON.hpp" @@ -184,10 +185,10 @@ void TableData::init() { for (nlohmann::json::iterator _lst = listings.begin(); _lst != listings.end(); _lst++) { auto lst = _lst.value(); VendorListing vListing = { lst["m_iSortNumber"], lst["m_iItemType"], lst["m_iitemID"] }; - ItemManager::VendorTables[lst["m_iNpcNumber"]].push_back(vListing); + Vendor::VendorTables[lst["m_iNpcNumber"]].push_back(vListing); } - std::cout << "[INFO] Loaded " << ItemManager::VendorTables.size() << " vendor tables" << std::endl; + std::cout << "[INFO] Loaded " << Vendor::VendorTables.size() << " vendor tables" << std::endl; // load crocpot entries nlohmann::json crocs = xdtData["m_pCombiningTable"]["m_pCombiningData"]; diff --git a/src/Trading.cpp b/src/Trading.cpp new file mode 100644 index 0000000..12bdccf --- /dev/null +++ b/src/Trading.cpp @@ -0,0 +1,432 @@ +#include "Trading.hpp" +#include "PlayerManager.hpp" + +bool doTrade(Player* plr, Player* plr2) { + // init dummy inventories + sItemBase plrInven[AINVEN_COUNT]; + sItemBase plr2Inven[AINVEN_COUNT]; + memcpy(plrInven, plr->Inven, AINVEN_COUNT * sizeof(sItemBase)); + memcpy(plr2Inven, plr2->Inven, AINVEN_COUNT * sizeof(sItemBase)); + + for (int i = 0; i < 5; i++) { + // remove items offered by us + if (plr->Trade[i].iID != 0) { + if (plrInven[plr->Trade[i].iInvenNum].iID == 0 + || plr->Trade[i].iID != plrInven[plr->Trade[i].iInvenNum].iID + || plr->Trade[i].iType != plrInven[plr->Trade[i].iInvenNum].iType) // pulling a fast one on us + return false; + + if (plr->Trade[i].iOpt < 1) { + std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl; + plr->Trade[i].iOpt = 1; + } + + // for stacked items + plrInven[plr->Trade[i].iInvenNum].iOpt -= plr->Trade[i].iOpt; + + if (plrInven[plr->Trade[i].iInvenNum].iOpt == 0) { + plrInven[plr->Trade[i].iInvenNum].iID = 0; + plrInven[plr->Trade[i].iInvenNum].iType = 0; + plrInven[plr->Trade[i].iInvenNum].iOpt = 0; + } else if (plrInven[plr->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt + return false; + } + } + + if (plr2->Trade[i].iID != 0) { + if (plr2Inven[plr2->Trade[i].iInvenNum].iID == 0 + || plr2->Trade[i].iID != plr2Inven[plr2->Trade[i].iInvenNum].iID + || plr2->Trade[i].iType != plr2Inven[plr2->Trade[i].iInvenNum].iType) // pulling a fast one on us + return false; + + if (plr2->Trade[i].iOpt < 1) { + std::cout << "[WARN] Player tried trading an iOpt < 1 amount" << std::endl; + plr2->Trade[i].iOpt = 1; + } + + // for stacked items + plr2Inven[plr2->Trade[i].iInvenNum].iOpt -= plr2->Trade[i].iOpt; + + if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt == 0) { + plr2Inven[plr2->Trade[i].iInvenNum].iID = 0; + plr2Inven[plr2->Trade[i].iInvenNum].iType = 0; + plr2Inven[plr2->Trade[i].iInvenNum].iOpt = 0; + } else if (plr2Inven[plr2->Trade[i].iInvenNum].iOpt < 0) { // another dupe attempt + return false; + } + } + + // add items offered to us + if (plr2->Trade[i].iID != 0) { + for (int n = 0; n < AINVEN_COUNT; n++) { + if (plrInven[n].iID == 0) { + plrInven[n].iID = plr2->Trade[i].iID; + plrInven[n].iType = plr2->Trade[i].iType; + plrInven[n].iOpt = plr2->Trade[i].iOpt; + plr2->Trade[i].iInvenNum = n; + break; + } + + if (n >= AINVEN_COUNT - 1) + return false; // not enough space + } + } + + if (plr->Trade[i].iID != 0) { + for (int n = 0; n < AINVEN_COUNT; n++) { + if (plr2Inven[n].iID == 0) { + plr2Inven[n].iID = plr->Trade[i].iID; + plr2Inven[n].iType = plr->Trade[i].iType; + plr2Inven[n].iOpt = plr->Trade[i].iOpt; + plr->Trade[i].iInvenNum = n; + break; + } + + if (n >= AINVEN_COUNT - 1) + return false; // not enough space + } + } + } + + // if everything went well, back into player inventory it goes + memcpy(plr->Inven, plrInven, AINVEN_COUNT * sizeof(sItemBase)); + memcpy(plr2->Inven, plr2Inven, AINVEN_COUNT * sizeof(sItemBase)); + + return true; +} + +static void tradeOffer(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_OFFER* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER*)data->buf; + + CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(otherSock); + + if (plr->isTrading) { + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); + resp.iID_Request = pacdat->iID_To; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); + return; // prevent trading with a player already trading + } + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER)); +} + +static void tradeOfferAccept(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT*)data->buf; + + CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(sock); + Player* plr2 = PlayerManager::getPlayer(otherSock); + + if (plr2->isTrading) { + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); + resp.iID_Request = pacdat->iID_From; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); + return; // prevent trading with a player already trading + } + + // clearing up trade slots + plr->moneyInTrade = 0; + plr2->moneyInTrade = 0; + memset(&plr->Trade, 0, sizeof(plr->Trade)); + memset(&plr2->Trade, 0, sizeof(plr2->Trade)); + + // marking players as traders + plr->isTrading = true; + plr2->isTrading = true; + + // marking players as unconfirmed + plr->isTradeConfirm = false; + plr2->isTradeConfirm = false; + + // inform the other player that offer is accepted + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC)); + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC)); +} + +static void tradeOfferRefusal(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL*)data->buf; + + CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL)); +} + +static void tradeConfirm(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_CONFIRM* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM*)data->buf; + + CNSocket* otherSock; // weird flip flop because we need to know who the other player is + if (pacdat->iID_Request == pacdat->iID_From) + otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + else + otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(sock); + Player* plr2 = PlayerManager::getPlayer(otherSock); + + if (!(plr->isTrading && plr2->isTrading)) { // both players must be trading + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp); + resp.iID_Request = plr2->iID; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); + resp.iID_Request = plr->iID; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); + return; + } + + // send the confirm packet + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); + + if (!(plr2->isTradeConfirm)) { + plr->isTradeConfirm = true; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); + return; + } + + // both players are no longer trading + plr->isTrading = false; + plr2->isTrading = false; + plr->isTradeConfirm = false; + plr2->isTradeConfirm = false; + + if (doTrade(plr, plr2)) { // returns false if not enough slots + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, resp2); + resp2.iID_Request = pacdat->iID_Request; + resp2.iID_From = pacdat->iID_From; + resp2.iID_To = pacdat->iID_To; + plr->money = plr->money + plr2->moneyInTrade - plr->moneyInTrade; + resp2.iCandy = plr->money; + memcpy(resp2.Item, plr2->Trade, sizeof(plr2->Trade)); + memcpy(resp2.ItemStay, plr->Trade, sizeof(plr->Trade)); + + sock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC)); + + plr2->money = plr2->money + plr->moneyInTrade - plr2->moneyInTrade; + resp2.iCandy = plr2->money; + memcpy(resp2.Item, plr->Trade, sizeof(plr->Trade)); + memcpy(resp2.ItemStay, plr2->Trade, sizeof(plr2->Trade)); + + otherSock->sendPacket((void*)&resp2, P_FE2CL_REP_PC_TRADE_CONFIRM_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_SUCC)); + } else { + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, resp); + resp.iID_Request = plr2->iID; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); + resp.iID_Request = plr->iID; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); + return; + } +} + +static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL* pacdat = (sP_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL*)data->buf; + + CNSocket* otherSock; // weird flip flop because we need to know who the other player is + if (pacdat->iID_Request == pacdat->iID_From) + otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + else + otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(sock); + Player* plr2 = PlayerManager::getPlayer(otherSock); + + // both players are not trading nor are in a confirmed state + plr->isTrading = false; + plr->isTradeConfirm = false; + plr2->isTrading = false; + plr2->isTradeConfirm = false; + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL)); +} + +static void tradeRegisterItem(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_REGISTER*)data->buf; + + if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4) + return; // sanity check, there are only 5 trade slots + + CNSocket* otherSock; // weird flip flop because we need to know who the other player is + if (pacdat->iID_Request == pacdat->iID_From) + otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + else + otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(sock); + plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item; + plr->isTradeConfirm = false; + + // since you can spread items like gumballs over multiple slots, we need to count them all + // to make sure the inventory shows the right value during trade. + int count = 0; + for (int i = 0; i < 5; i++) { + if (plr->Trade[i].iInvenNum == pacdat->Item.iInvenNum) + count += plr->Trade[i].iOpt; + } + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + resp.TradeItem = pacdat->Item; + resp.InvenItem = pacdat->Item; + resp.InvenItem.iOpt = plr->Inven[pacdat->Item.iInvenNum].iOpt - count; // subtract this count + + if (resp.InvenItem.iOpt < 0) // negative count items, doTrade() will block this later on + std::cout << "[WARN] tradeRegisterItem: an item went negative count client side." << std::endl; + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC)); + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC)); +} + +static void tradeUnregisterItem(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER*)data->buf; + + if (pacdat->Item.iSlotNum < 0 || pacdat->Item.iSlotNum > 4) + return; // sanity check, there are only 5 trade slots + + CNSocket* otherSock; // weird flip flop because we need to know who the other player is + if (pacdat->iID_Request == pacdat->iID_From) + otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + else + otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + Player* plr = PlayerManager::getPlayer(sock); + plr->isTradeConfirm = false; + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + resp.TradeItem = pacdat->Item; + resp.InvenItem = plr->Trade[pacdat->Item.iSlotNum]; + + memset(&plr->Trade[pacdat->Item.iSlotNum], 0, sizeof(plr->Trade[pacdat->Item.iSlotNum])); // clean up item slot + + // since you can spread items like gumballs over multiple slots, we need to count them all + // to make sure the inventory shows the right value during trade. + int count = 0; + for (int i = 0; i < 5; i++) { + if (plr->Trade[i].iInvenNum == resp.InvenItem.iInvenNum) + count += plr->Trade[i].iOpt; + } + + resp.InvenItem.iOpt = plr->Inven[resp.InvenItem.iInvenNum].iOpt - count; // subtract this count + + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC)); + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC)); +} + +static void tradeRegisterCash(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER)) + return; // ignore the malformed packet + + sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER* pacdat = (sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER*)data->buf; + + Player* plr = PlayerManager::getPlayer(sock); + + if (pacdat->iCandy < 0 || pacdat->iCandy > plr->money) + return; // famous glitch, begone + + CNSocket* otherSock; // weird flip flop because we need to know who the other player is + if (pacdat->iID_Request == pacdat->iID_From) + otherSock = PlayerManager::getSockFromID(pacdat->iID_To); + else + otherSock = PlayerManager::getSockFromID(pacdat->iID_From); + + if (otherSock == nullptr) + return; + + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + resp.iCandy = pacdat->iCandy; + sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC)); + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC)); + + plr->moneyInTrade = pacdat->iCandy; + plr->isTradeConfirm = false; +} + +void Trading::init() { + // Trade handlers + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, tradeOffer); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, tradeOfferAccept); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, tradeOfferRefusal); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, tradeConfirm); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, tradeConfirmCancel); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, tradeRegisterItem); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, tradeUnregisterItem); + REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, tradeRegisterCash); +} \ No newline at end of file diff --git a/src/Trading.hpp b/src/Trading.hpp new file mode 100644 index 0000000..46786ba --- /dev/null +++ b/src/Trading.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "ItemManager.hpp" + +namespace Trading { + void init(); +} \ No newline at end of file diff --git a/src/Vendor.cpp b/src/Vendor.cpp new file mode 100644 index 0000000..04c0741 --- /dev/null +++ b/src/Vendor.cpp @@ -0,0 +1,3 @@ +#include "Vendor.hpp" + +std::map> Vendor::VendorTables; \ No newline at end of file diff --git a/src/Vendor.hpp b/src/Vendor.hpp new file mode 100644 index 0000000..a06dbb1 --- /dev/null +++ b/src/Vendor.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "ItemManager.hpp" + +struct VendorListing { + int sort, type, iID; +}; + +namespace Vendor { + extern std::map> VendorTables; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0f69a16..4d525dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "GroupManager.hpp" #include "Monitor.hpp" #include "RacingManager.hpp" +#include "Trading.hpp" #include "settings.hpp" @@ -110,6 +111,7 @@ int main() { GroupManager::init(); RacingManager::init(); Database::open(); + Trading::init(); switch (settings::EVENTMODE) { case 0: break; // no event