diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index b4328cb..a44bbcb 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -32,15 +32,15 @@ void ItemManager::init() { // Bank REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_BANK_OPEN, itemBankOpenHandler); // Trade handlers - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER, itemTradeOfferHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_ACCEPT, itemTradeOfferAcceptHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL, itemTradeOfferRefusalHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM, itemTradeConfirmHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CONFIRM_CANCEL, itemTradeConfirmCancelHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_REGISTER, itemTradeRegisterItemHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, itemTradeUnregisterItemHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, itemTradeRegisterCashHandler); - REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, itemTradeChatHandler); + 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_PC_TRADE_EMOTES_CHAT, tradeChat); REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler); } @@ -332,284 +332,155 @@ 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::itemTradeOfferHandler(CNSocket* sock, CNPacketData* data) { +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; - int iID_Check; + CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_To); - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } + if (otherSock == nullptr) + return; - CNSocket* otherSock = sock; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - break; - } - } - - Player* plr = PlayerManager::getPlayer(sock); + 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::itemTradeOfferAcceptHandler(CNSocket* sock, CNPacketData* data) { +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; - int iID_Check; + CNSocket* otherSock = PlayerManager::getSockFromID(pacdat->iID_From); - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = sock; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - break; - } - } - - 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; - - // Clearing up trade slots + 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_To; + 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 } - plr->isTrading = true; - plr2->isTrading = true; - plr->isTradeConfirm = false; - plr2->isTradeConfirm = false; - + // clearing up trade slots plr->moneyInTrade = 0; plr2->moneyInTrade = 0; - memset(&plr->Trade, 0, sizeof(plr->Trade)); memset(&plr2->Trade, 0, sizeof(plr2->Trade)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_OFFER_SUCC, sizeof(sP_FE2CL_REP_PC_TRADE_OFFER_SUCC)); -} + // marking players as traders + plr->isTrading = true; + plr2->isTrading = true; -void ItemManager::itemTradeOfferRefusalHandler(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; - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_OFFER_REFUSAL, resp); + // 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; - int iID_Check; + 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)); +} - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } +void ItemManager::tradeOfferRefusal(CNSocket* sock, CNPacketData* data) { + if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL)) + return; // ignore the malformed packet - CNSocket* otherSock = sock; + sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL* pacdat = (sP_CL2FE_REQ_PC_TRADE_OFFER_REFUSAL*)data->buf; - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - break; - } - } + 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::itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data) { +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; - int iID_Check; + 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 (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = sock; - - for (auto& pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - break; - } - } + if (otherSock == nullptr) + return; Player* plr = PlayerManager::getPlayer(sock); Player* plr2 = PlayerManager::getPlayer(otherSock); - - if (plr2->isTradeConfirm) { - - plr->isTrading = false; - plr2->isTrading = false; - plr->isTradeConfirm = false; - plr2->isTradeConfirm = false; - - // Check if we have enough free slots - int freeSlots = 0; - int freeSlotsNeeded = 0; - int freeSlots2 = 0; - int freeSlotsNeeded2 = 0; - - for (int i = 0; i < AINVEN_COUNT; i++) { - if (plr->Inven[i].iID == 0) - freeSlots++; - } - - for (int i = 0; i < 5; i++) { - if (plr->Trade[i].iID != 0) - freeSlotsNeeded++; - } - - for (int i = 0; i < AINVEN_COUNT; i++) { - if (plr2->Inven[i].iID == 0) - freeSlots2++; - } - - for (int i = 0; i < 5; i++) { - if (plr2->Trade[i].iID != 0) - freeSlotsNeeded2++; - } - - if (freeSlotsNeeded2 - freeSlotsNeeded > freeSlots) { - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, 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_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_ABORT, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_ABORT)); - return; // Fail trade because of the lack of slots - } - - if (freeSlotsNeeded - freeSlotsNeeded2 > freeSlots2) { - 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; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM_CANCEL)); - return; // Fail trade because of the lack of slots - } - - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CONFIRM, resp); - - resp.iID_Request = pacdat->iID_Request; + + 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; + } - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); - // ^^ this is a must have or else the player won't accept a succ packet for some reason + // 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)); - for (int i = 0; i < freeSlotsNeeded; i++) { - plr->Inven[plr->Trade[i].iInvenNum].iID = 0; - plr->Inven[plr->Trade[i].iInvenNum].iType = 0; - plr->Inven[plr->Trade[i].iInvenNum].iOpt = 0; - } + if (!(plr2->isTradeConfirm)) { + plr->isTradeConfirm = true; + otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); + return; + } - for (int i = 0; i < freeSlotsNeeded2; i++) { - plr2->Inven[plr2->Trade[i].iInvenNum].iID = 0; - plr2->Inven[plr2->Trade[i].iInvenNum].iType = 0; - plr2->Inven[plr2->Trade[i].iInvenNum].iOpt = 0; - } - - for (int i = 0; i < AINVEN_COUNT; i++) { - if (freeSlotsNeeded <= 0) - break; - - if (plr2->Inven[i].iID == 0) { - - plr2->Inven[i].iID = plr->Trade[freeSlotsNeeded - 1].iID; - plr2->Inven[i].iType = plr->Trade[freeSlotsNeeded - 1].iType; - plr2->Inven[i].iOpt = plr->Trade[freeSlotsNeeded - 1].iOpt; - plr->Trade[freeSlotsNeeded - 1].iInvenNum = i; - freeSlotsNeeded--; - } - } - - for (int i = 0; i < AINVEN_COUNT; i++) { - if (freeSlotsNeeded2 <= 0) - break; - - if (plr->Inven[i].iID == 0) { - - plr->Inven[i].iID = plr2->Trade[freeSlotsNeeded2 - 1].iID; - plr->Inven[i].iType = plr2->Trade[freeSlotsNeeded2 - 1].iType; - plr->Inven[i].iOpt = plr2->Trade[freeSlotsNeeded2 - 1].iOpt; - plr2->Trade[freeSlotsNeeded2 - 1].iInvenNum = i; - freeSlotsNeeded2--; - } - } + // 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)); @@ -617,152 +488,223 @@ void ItemManager::itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data) { 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, resp); - - resp.iID_Request = pacdat->iID_Request; + 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; - - plr->isTradeConfirm = true; - - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); - otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_CONFIRM, sizeof(sP_FE2CL_REP_PC_TRADE_CONFIRM)); + 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; } } -void ItemManager::itemTradeConfirmCancelHandler(CNSocket* sock, CNPacketData* data) { +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) // pulling a fast one on us + return false; + + // 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) // pulling a fast one on us + return false; + + // 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; - 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; + 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); - int iID_Check; - - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = sock; - - for (auto pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - } - } + 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::itemTradeRegisterItemHandler(CNSocket* sock, CNPacketData* data) { +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 + return; // sanity check, there are only 5 trade slots - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_REGISTER_SUCC, resp); + 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); - 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; - - int iID_Check; - - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = sock; - - for (auto pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - } - } + if (otherSock == nullptr) + return; Player* plr = PlayerManager::getPlayer(sock); plr->Trade[pacdat->Item.iSlotNum] = pacdat->Item; plr->isTradeConfirm = false; - 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::itemTradeUnregisterItemHandler(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; - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_ITEM_UNREGISTER_SUCC, resp); + // 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); - resp.InvenItem = plr->Trade[pacdat->Item.iSlotNum]; plr->isTradeConfirm = false; - int iID_Check; + 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]; - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; + 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; } - CNSocket* otherSock = sock; - - for (auto pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - } - } - - int temp_num = pacdat->Item.iSlotNum; - - if (temp_num >= 0 && temp_num <= 4) { - plr->Trade[temp_num].iID = 0; - plr->Trade[temp_num].iType = 0; - plr->Trade[temp_num].iOpt = 0; - plr->Trade[temp_num].iInvenNum = 0; - plr->Trade[temp_num].iSlotNum = 0; - } + 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::itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data) { +void ItemManager::tradeRegisterCash(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_CASH_REGISTER)) return; // ignore the malformed packet @@ -773,71 +715,57 @@ void ItemManager::itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* dat if (pacdat->iCandy < 0 || pacdat->iCandy > plr->money) return; // famous glitch, begone - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_CASH_REGISTER_SUCC, resp); + 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; - - int iID_Check; - - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = sock; - - for (auto pair : PlayerManager::players) { - if (pair.second->iID == iID_Check) { - otherSock = pair.first; - } - } + 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; - - 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)); } -void ItemManager::itemTradeChatHandler(CNSocket* sock, CNPacketData* data) { +void ItemManager::tradeChat(CNSocket* sock, CNPacketData* data) { if (data->size != sizeof(sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT)) return; // malformed packet sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT* pacdat = (sP_CL2FE_REQ_PC_TRADE_EMOTES_CHAT*)data->buf; - INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp); - Player *plr = PlayerManager::getPlayer(sock); + 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); - resp.iID_Request = pacdat->iID_Request; - resp.iID_From = pacdat->iID_From; - resp.iID_To = pacdat->iID_To; - - std::string fullChat = ChatManager::sanitizeText(U16toU8(pacdat->szFreeChat)); - U8toU16(fullChat, resp.szFreeChat, sizeof(resp.szFreeChat)); - - int iID_Check; - if (pacdat->iID_Request == pacdat->iID_From) { - iID_Check = pacdat->iID_To; - } else { - iID_Check = pacdat->iID_From; - } - - CNSocket* otherSock = PlayerManager::getSockFromID(iID_Check);; if (otherSock == nullptr) return; Player *otherPlr = PlayerManager::getPlayer(otherSock); + INITSTRUCT(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT, resp); + Player *plr = PlayerManager::getPlayer(sock); + resp.iID_Request = pacdat->iID_Request; + resp.iID_From = pacdat->iID_From; + resp.iID_To = pacdat->iID_To; + std::string fullChat = ChatManager::sanitizeText(U16toU8(pacdat->szFreeChat)); + U8toU16(fullChat, resp.szFreeChat, sizeof(resp.szFreeChat)); + std::string logLine = "[TradeChat] " + PlayerManager::getPlayerName(plr) + " (to " + PlayerManager::getPlayerName(otherPlr) + "): " + fullChat; std::cout << logLine << std::endl; ChatManager::dump.push_back(logLine); resp.iEmoteCode = pacdat->iEmoteCode; - sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); } diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index d726a2e..4feede8 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -44,15 +44,16 @@ namespace ItemManager { void itemUseHandler(CNSocket* sock, CNPacketData* data); // Bank void itemBankOpenHandler(CNSocket* sock, CNPacketData* data); - void itemTradeOfferHandler(CNSocket* sock, CNPacketData* data); - void itemTradeOfferAcceptHandler(CNSocket* sock, CNPacketData* data); - void itemTradeOfferRefusalHandler(CNSocket* sock, CNPacketData* data); - void itemTradeConfirmHandler(CNSocket* sock, CNPacketData* data); - void itemTradeConfirmCancelHandler(CNSocket* sock, CNPacketData* data); - void itemTradeRegisterItemHandler(CNSocket* sock, CNPacketData* data); - void itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data); - void itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data); - void itemTradeChatHandler(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 tradeChat(CNSocket* sock, CNPacketData* data); void chestOpenHandler(CNSocket* sock, CNPacketData* data); // crate opening logic with all helper functions