mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 13:30:06 +00:00
Keep track of sold items so we can validate buyback packets
This commit is contained in:
parent
0fbdb1dad2
commit
ffe5947925
@ -121,26 +121,25 @@ void NPCManager::npcVendorBuy(CNSocket* sock, CNPacketData* data) {
|
|||||||
sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY* req = (sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY*)data->buf;
|
sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY* req = (sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY*)data->buf;
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
ItemManager::Item* item = ItemManager::getItemData(req->Item.iID, req->Item.iType);
|
// prepare fail packet
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, failResp);
|
||||||
|
failResp.iErrorCode = 0;
|
||||||
|
|
||||||
if (item == nullptr) {
|
ItemManager::Item* itemDat = ItemManager::getItemData(req->Item.iID, req->Item.iType);
|
||||||
|
|
||||||
|
if (itemDat == nullptr) {
|
||||||
std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found (buy)" << std::endl;
|
std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found (buy)" << std::endl;
|
||||||
// NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side.
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, failResp);
|
|
||||||
failResp.iErrorCode = 0;
|
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemCost = item->buyPrice * (item->stackSize > 1 ? req->Item.iOpt : 1);
|
int itemCost = itemDat->buyPrice * (itemDat->stackSize > 1 ? req->Item.iOpt : 1);
|
||||||
int slot = ItemManager::findFreeSlot(plr);
|
int slot = ItemManager::findFreeSlot(plr);
|
||||||
if (itemCost > plr->money || slot == -1) {
|
if (itemCost > plr->money || slot == -1 || req->Item.iOpt > itemDat->stackSize) {
|
||||||
// NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side.
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, failResp);
|
|
||||||
failResp.iErrorCode = 0;
|
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if vehicle
|
// if vehicle
|
||||||
if (req->Item.iType == 10) {
|
if (req->Item.iType == 10) {
|
||||||
// set time limit: current time + 7days
|
// set time limit: current time + 7days
|
||||||
@ -184,14 +183,14 @@ void NPCManager::npcVendorSell(CNSocket* sock, CNPacketData* data) {
|
|||||||
sItemBase* item = &plr->Inven[req->iInvenSlotNum];
|
sItemBase* item = &plr->Inven[req->iInvenSlotNum];
|
||||||
ItemManager::Item* itemData = ItemManager::getItemData(item->iID, item->iType);
|
ItemManager::Item* itemData = ItemManager::getItemData(item->iID, item->iType);
|
||||||
|
|
||||||
if (itemData == nullptr || !itemData->sellable || plr->Inven[req->iInvenSlotNum].iOpt < req->iItemCnt) { // sanity + sellable check
|
if (itemData == nullptr || !itemData->sellable || item->iOpt < req->iItemCnt) { // sanity + sellable check
|
||||||
std::cout << "[WARN] Item id " << item->iID << " with type " << item->iType << " not found (sell)" << std::endl;
|
std::cout << "[WARN] Item id " << item->iID << " with type " << item->iType << " not found (sell)" << std::endl;
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail to sell croc-potted items
|
// fail to sell croc-potted items
|
||||||
if (plr->Inven[req->iInvenSlotNum].iOpt >= 1 << 16) {
|
if (item->iOpt >= 1 << 16) {
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -201,22 +200,25 @@ void NPCManager::npcVendorSell(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC, resp);
|
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_SELL_SUCC, resp);
|
||||||
|
|
||||||
int sellValue = itemData->sellPrice * req->iItemCnt;
|
|
||||||
|
|
||||||
// increment taros
|
// increment taros
|
||||||
plr->money = plr->money + sellValue;
|
plr->money += itemData->sellPrice * req->iItemCnt;
|
||||||
|
|
||||||
// modify item
|
// modify item
|
||||||
if (plr->Inven[req->iInvenSlotNum].iOpt - req->iItemCnt > 0) { // selling part of a stack
|
if (item->iOpt - req->iItemCnt > 0) { // selling part of a stack
|
||||||
item->iOpt -= req->iItemCnt;
|
item->iOpt -= req->iItemCnt;
|
||||||
original.iOpt = req->iItemCnt;
|
original.iOpt = req->iItemCnt;
|
||||||
} else { // selling entire slot
|
} else { // selling entire slot
|
||||||
item->iID = 0;
|
// make sure it's fully zeroed, even the padding and non-104 members
|
||||||
item->iOpt = 0;
|
memset(item, 0, sizeof(*item));
|
||||||
item->iType = 0;
|
|
||||||
item->iTimeLimit = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add to buyback list
|
||||||
|
plr->buyback->push_back(original);
|
||||||
|
// forget oldest member if there's more than 5
|
||||||
|
if (plr->buyback->size() > 5)
|
||||||
|
plr->buyback->erase(plr->buyback->begin());
|
||||||
|
//std::cout << (int)plr->buyback->size() << " items in buyback\n";
|
||||||
|
|
||||||
// response parameters
|
// response parameters
|
||||||
resp.iInvenSlotNum = req->iInvenSlotNum;
|
resp.iInvenSlotNum = req->iInvenSlotNum;
|
||||||
resp.iCandy = plr->money;
|
resp.iCandy = plr->money;
|
||||||
@ -233,24 +235,57 @@ void NPCManager::npcVendorBuyback(CNSocket* sock, CNPacketData* data) {
|
|||||||
sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY* req = (sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY*)data->buf;
|
sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY* req = (sP_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY*)data->buf;
|
||||||
Player* plr = PlayerManager::getPlayer(sock);
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
ItemManager::Item* item = ItemManager::getItemData(req->Item.iID, req->Item.iType);
|
// prepare fail packet
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, failResp);
|
||||||
|
failResp.iErrorCode = 0;
|
||||||
|
|
||||||
if (item == nullptr) {
|
//std::cout << "buying back from index " << (int)req->iListID << " into " << (int)req->iInvenSlotNum <<
|
||||||
std::cout << "[WARN] Item id " << req->Item.iID << " with type " << req->Item.iType << " not found (rebuy)" << std::endl;
|
// " from " << (int)req->iNPC_ID << " (vendor = " << (int)req->iVendorID << ")\n";
|
||||||
// NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side.
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, failResp);
|
int idx = req->iListID - 1;
|
||||||
failResp.iErrorCode = 0;
|
|
||||||
|
// sanity check
|
||||||
|
if (idx < 0 || idx >= plr->buyback->size()) {
|
||||||
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the item out of the buyback list
|
||||||
|
sItemBase item = (*plr->buyback)[idx];
|
||||||
|
/*
|
||||||
|
* NOTE: The client sends the index of the exact item the user clicked on.
|
||||||
|
* We then operate on that item, but we remove the *first* identical item
|
||||||
|
* from the buyback list, instead of the one at the supplied index.
|
||||||
|
*
|
||||||
|
* This was originally a mistake on my part, but it turns out the client
|
||||||
|
* does the exact same thing, so this *is* the correct thing to do to keep
|
||||||
|
* them in sync.
|
||||||
|
*/
|
||||||
|
for (auto it = plr->buyback->begin(); it != plr->buyback->end(); it++) {
|
||||||
|
/*
|
||||||
|
* XXX: we really need a standard item comparison function that
|
||||||
|
* will work properly across all builds (ex. with iSerial)
|
||||||
|
*/
|
||||||
|
if (it->iType == item.iType && it->iID == item.iID && it->iOpt == item.iOpt
|
||||||
|
&& it->iTimeLimit == item.iTimeLimit) {
|
||||||
|
plr->buyback->erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//std::cout << (int)plr->buyback->size() << " items in buyback\n";
|
||||||
|
|
||||||
|
ItemManager::Item* itemDat = ItemManager::getItemData(item.iID, item.iType);
|
||||||
|
|
||||||
|
if (itemDat == nullptr) {
|
||||||
|
std::cout << "[WARN] Item id " << item.iID << " with type " << item.iType << " not found (rebuy)" << std::endl;
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sell price is used on rebuy. ternary identifies stacked items
|
// sell price is used on rebuy. ternary identifies stacked items
|
||||||
int itemCost = item->sellPrice * (item->stackSize > 1 ? req->Item.iOpt : 1);
|
int itemCost = itemDat->sellPrice * (itemDat->stackSize > 1 ? item.iOpt : 1);
|
||||||
int slot = ItemManager::findFreeSlot(plr);
|
int slot = ItemManager::findFreeSlot(plr);
|
||||||
if (itemCost > plr->money || slot == -1) {
|
if (itemCost > plr->money || slot == -1) {
|
||||||
// NOTE: VENDOR_ITEM_BUY_FAIL is not actually handled client-side.
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, failResp);
|
|
||||||
failResp.iErrorCode = 0;
|
|
||||||
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL));
|
sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_FAIL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -261,13 +296,13 @@ void NPCManager::npcVendorBuyback(CNSocket* sock, CNPacketData* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plr->money = plr->money - itemCost;
|
plr->money = plr->money - itemCost;
|
||||||
plr->Inven[slot] = req->Item;
|
plr->Inven[slot] = item;
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, resp);
|
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, resp);
|
||||||
// response parameters
|
// response parameters
|
||||||
resp.iCandy = plr->money;
|
resp.iCandy = plr->money;
|
||||||
resp.iInvenSlotNum = slot;
|
resp.iInvenSlotNum = slot;
|
||||||
resp.Item = req->Item;
|
resp.Item = item;
|
||||||
|
|
||||||
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC));
|
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_RESTORE_BUY_SUCC));
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,10 @@ struct Player {
|
|||||||
uint64_t iFirstUseFlag[2];
|
uint64_t iFirstUseFlag[2];
|
||||||
|
|
||||||
ChunkPos chunkPos;
|
ChunkPos chunkPos;
|
||||||
std::set<Chunk*>* viewableChunks;
|
std::set<Chunk*> *viewableChunks;
|
||||||
time_t lastHeartbeat;
|
time_t lastHeartbeat;
|
||||||
|
|
||||||
int suspicionRating;
|
int suspicionRating;
|
||||||
time_t lastShot;
|
time_t lastShot;
|
||||||
|
std::vector<sItemBase> *buyback;
|
||||||
};
|
};
|
||||||
|
@ -63,6 +63,7 @@ void PlayerManager::addPlayer(CNSocket* key, Player plr) {
|
|||||||
p->chunkPos = std::make_tuple(0, 0, 0);
|
p->chunkPos = std::make_tuple(0, 0, 0);
|
||||||
p->viewableChunks = new std::set<Chunk*>();
|
p->viewableChunks = new std::set<Chunk*>();
|
||||||
p->lastHeartbeat = 0;
|
p->lastHeartbeat = 0;
|
||||||
|
p->buyback = new std::vector<sItemBase>();
|
||||||
|
|
||||||
std::cout << getPlayerName(p) << " has joined!" << std::endl;
|
std::cout << getPlayerName(p) << " has joined!" << std::endl;
|
||||||
std::cout << players.size() << " players" << std::endl;
|
std::cout << players.size() << " players" << std::endl;
|
||||||
@ -89,6 +90,7 @@ void PlayerManager::removePlayer(CNSocket* key) {
|
|||||||
|
|
||||||
std::cout << getPlayerName(plr) << " has left!" << std::endl;
|
std::cout << getPlayerName(plr) << " has left!" << std::endl;
|
||||||
|
|
||||||
|
delete plr->buyback;
|
||||||
delete plr->viewableChunks;
|
delete plr->viewableChunks;
|
||||||
delete plr;
|
delete plr;
|
||||||
players.erase(key);
|
players.erase(key);
|
||||||
|
Loading…
Reference in New Issue
Block a user