From 5a5890846258c8892e4c199f78d133caf470bbd9 Mon Sep 17 00:00:00 2001 From: kamilprzyb Date: Tue, 22 Sep 2020 13:16:09 +0200 Subject: [PATCH] vehicle has a proper expiration day when bought, implemented checking expired vehicles while login --- src/Database.cpp | 36 +++++++++++++++++++++++++++++++++++- src/Database.hpp | 1 + src/ItemManager.cpp | 32 ++++++++++++++++++++++++++++++++ src/ItemManager.hpp | 1 + src/NPCManager.cpp | 5 +++++ src/Player.hpp | 2 ++ src/PlayerManager.cpp | 3 +++ 7 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/Database.cpp b/src/Database.cpp index 497d4e0..92ab75b 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -432,8 +432,9 @@ Player Database::DbToPlayer(DbPlayer player) { result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2; Database::getInventory(&result); + Database::removeExpiredVehicles(&result); Database::getNanos(&result); - Database::getQuests(&result); + Database::getQuests(&result); std::vector::iterator it = player.QuestFlag.begin(); for (int i = 0; i < 16; i++) @@ -606,6 +607,39 @@ void Database::getInventory(Player* player) { player->QInven[current.slot - AEQUIP_COUNT - AINVEN_COUNT - ABANK_COUNT] = toSet; } + +} + +void Database::removeExpiredVehicles(Player* player) { + uint64_t currentTime = getTime(); + //remove from bank immediately + for (int i = 0; i < ABANK_COUNT; i++) { + if (player->Bank[i].iType == 10 && player->Bank[i].iTimeLimit < currentTime) + player->Bank[i] = {}; + } + //for the rest, we want to leave only 1 expired vehicle on player to delete it with the client packet + std::vector toRemove; + + //equiped vehicle + if (player->Equip[8].iOpt > 0 && player->Equip[8].iTimeLimit < currentTime) + { + toRemove.push_back(&player->Equip[8]); + player->toRemoveVehicle.eIL = 0; + player->toRemoveVehicle.iSlotNum = 8; + } + //inventory + for (int i = 0; i < AINVEN_COUNT; i++) { + if (player->Inven[i].iType == 10 && player->Inven[i].iTimeLimit < currentTime) { + toRemove.push_back(&player->Inven[i]); + player->toRemoveVehicle.eIL = 1; + player->toRemoveVehicle.iSlotNum = i; + } + } + + //delete all but one vehicles, leave last one for ceremonial deletion + for (int i = 1; i < toRemove.size(); i++) { + memset(toRemove[i-1], 0, sizeof(sItemBase)); + } } void Database::getNanos(Player* player) { diff --git a/src/Database.hpp b/src/Database.hpp index c4b3a5e..9c90f09 100644 --- a/src/Database.hpp +++ b/src/Database.hpp @@ -126,6 +126,7 @@ namespace Database { void updateQuests(Player* player); void getInventory(Player* player); + void removeExpiredVehicles(Player* player); void getNanos(Player* player); void getQuests(Player* player); diff --git a/src/ItemManager.cpp b/src/ItemManager.cpp index 5e77c41..60e9f8b 100644 --- a/src/ItemManager.cpp +++ b/src/ItemManager.cpp @@ -767,3 +767,35 @@ Item* ItemManager::getItemData(int32_t id, int32_t type) { return &ItemData[std::pair(id, type)]; return nullptr; } + +void ItemManager::checkItemExpire(CNSocket* sock, Player* player) { + if (player->toRemoveVehicle.eIL == 0 && player->toRemoveVehicle.iSlotNum == 0) + return; + + /* prepare packet + * yes, this is a varadic packet, however analyzing client behavior and code + * it only checks takes the first item sent into account + * yes, this is very stupid + * therefore, we delete all but 1 expired vehicle while loading player + * to delete the last one here so player gets a notification + */ + + const size_t resplen = sizeof(sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM) + sizeof(sTimeLimitItemDeleteInfo2CL); + 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_PC_DELETE_TIME_LIMIT_ITEM* packet = (sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM*)respbuf; + sTimeLimitItemDeleteInfo2CL* itemData = (sTimeLimitItemDeleteInfo2CL*)(respbuf + sizeof(sP_FE2CL_PC_DELETE_TIME_LIMIT_ITEM)); + memset(respbuf, 0, resplen); + + packet->iItemListCount = 1; + itemData->eIL = player->toRemoveVehicle.eIL; + itemData->iSlotNum = player->toRemoveVehicle.iSlotNum; + sock->sendPacket((void*)&respbuf, P_FE2CL_PC_DELETE_TIME_LIMIT_ITEM, resplen); + + //delete serverside + if (player->toRemoveVehicle.eIL == 0) + memset(&player->Equip[8], 0, sizeof(sItemBase)); + else + memset(&player->Inven[player->toRemoveVehicle.iSlotNum], 0, sizeof(sItemBase)); +} diff --git a/src/ItemManager.hpp b/src/ItemManager.hpp index 7c627fb..7972734 100644 --- a/src/ItemManager.hpp +++ b/src/ItemManager.hpp @@ -47,4 +47,5 @@ namespace ItemManager { int findFreeSlot(Player *plr); Item* getItemData(int32_t id, int32_t type); + void checkItemExpire(CNSocket* sock, Player* player); } diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index ee0d815..bccb906 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -115,6 +115,11 @@ void NPCManager::npcVendorBuy(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&failResp, P_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL, sizeof(sP_FE2CL_REP_PC_VENDOR_ITEM_BUY_FAIL)); return; } + // if vehicle + if (req->Item.iType == 10) + // set time limit: current time + 7days + req->Item.iTimeLimit = getTime() + 604800; + if (slot != req->iInvenSlotNum) { // possible item stacking? diff --git a/src/Player.hpp b/src/Player.hpp index 18ca7e5..3510998 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -49,4 +49,6 @@ struct Player { int RemainingNPCCount[ACTIVE_MISSION_COUNT][3]; sItemBase QInven[AQINVEN_COUNT]; int32_t CurrentMissionID; + + sTimeLimitItemDeleteInfo2CL toRemoveVehicle; }; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index 98a5bd6..a4d0a5a 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -4,6 +4,7 @@ #include "CNShardServer.hpp" #include "CNShared.hpp" #include "MissionManager.hpp" +#include "ItemManager.hpp" #include "settings.hpp" @@ -283,6 +284,8 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&motd, P_FE2CL_PC_MOTD_LOGIN, sizeof(sP_FE2CL_PC_MOTD_LOGIN)); addPlayer(sock, plr); + //check if there is an expiring vehicle + ItemManager::checkItemExpire(sock, getPlayer(sock)); } void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) {