#include "stdafx.h" #ifdef ENABLE_IKASHOP_RENEWAL #include "Main.h" #include "Config.h" #include "DBManager.h" #include "QID.h" #include "Peer.h" #include "ClientManager.h" #include "IkarusShopCache.h" namespace ikashop { template bool IsItemExpired(const auto now, const T& item){ switch (static_cast(item.expiration)) { case ikashop::ExpirationType::EXPIRE_REAL_TIME_FIRST_USE: // checking activation only for real time first use if (item.alSockets[1] == 0) return false; case ikashop::ExpirationType::EXPIRE_REAL_TIME: // checking timestamp on socket 0 for both timers if (now < item.alSockets[0]) return false; // inserting into vector return true; default: break; } return false; } //SHOPS std::string CreateShopCacheUpdateItemQuery(const TShopItem& rItemPrice); std::string CreateShopCacheDeleteShopQuery(DWORD dwOwner); std::string CreateShopCacheDeleteShopItemQuery(DWORD dwOwner); #ifdef ENABLE_IKASHOP_ENTITIES std::string CreateShopCacheInsertShopQuery(DWORD dwOwnerID, DWORD dwDuration, const char* name, const TShopSpawn& spawn); #else std::string CreateShopCacheInsertShopQuery(DWORD dwOwnerID, DWORD dwDuration, const char* name); #endif std::string CreateShopCacheUpdateDurationQuery(DWORD dwOwnerID, DWORD dwDuration); std::string CreateShopCacheDeleteItemQuery(DWORD dwOwnerID, DWORD dwItemID); //SAFEBOX std::string CreateSafeboxCacheDeleteItemQuery(DWORD dwItem); std::string CreateSafeboxCacheInsertItemQuery(DWORD dwOwner, DWORD dwItemID, const TShopPlayerItem& item); std::string CreateSafeboxCacheUpdateValutes(DWORD dwOwner, const TValutesInfo& val); std::string CreateSafeboxCacheInsertSafeboxValutesQuery(DWORD dwOwnerID); std::string CreateSafeboxCacheUpdateValutesByAdding(DWORD dwOwner, const TValutesInfo& val); std::string CreateSafeboxCacheLoadItemsQuery(DWORD dwOwnerID); std::string CreateSafeboxCacheLoadValutesQuery(DWORD dwOwnerID); //OFFERS std::string CreateOfferCacheInsertOfferQuery(const TOfferInfo& rOffer); std::string CreateOfferCacheUpdateNotifiedQuery(DWORD dwOfferID); std::string CreateOfferCacheUpdateAcceptedQuery(DWORD dwOfferID); std::string CreateOfferCacheRemoveOfferByShopOwner(DWORD dwShopOwner); std::string CreateOfferCacheDeleteOfferQuery(DWORD dwOfferID); //AUCTION std::string CreateAuctionCacheAddAuctionQuery(const TAuctionInfo& auction); std::string CreateAuctionCacheAddOfferQuery(const TAuctionOfferInfo& auctionOffer); //std::string CreateAuctionCacheDeleteAuction(DWORD dwOwnerID); std::string CreateAuctionCacheDeleteAuctionOffers(DWORD dwOwnerID); std::string CreateAuctionCacheUpdateDurationQuery(const TAuctionInfo& info); /* CSHOPCACHE */ CShopCache::CShopCache() { } CShopCache::~CShopCache() { } CShopCache::SHOP_HANDLE CShopCache::Get(DWORD dwOwnerID) const { // searching shop cache auto it = m_shopsMap.find(dwOwnerID); if (it == m_shopsMap.end()) return nullptr; // not found return it->second; } bool CShopCache::AddItem(const TShopCacheItemInfo& item) { // searching shop cache auto cache = Get(item.owner); if(!cache) return false; // refreshing table datas std::string query = CreateShopCacheUpdateItemQuery(item); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_ADD_ITEM, 0, new SQueryInfoAddItem(item)); return true; } bool CShopCache::RemoveItem(DWORD dwOwnerID, DWORD dwItemID) { // searching for shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching for item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // skipping locked items if(it->second->lock) return false; // removing item from cache itemsmap.erase(it); // removing item from db std::string query = CreateShopCacheDeleteItemQuery(dwOwnerID, dwItemID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_REMOVE_ITEM, 0, NULL); return true; } void CShopCache::DeleteItem(DWORD owner, DWORD itemid) { // removing from table auto query = fmt::format("DELETE FROM `item` WHERE `id`={}", itemid); CDBManager::instance().AsyncQuery(query.c_str()); // removing from cache if (auto shop = Get(owner)) shop->itemsmap.erase(itemid); } bool CShopCache::SellItem(DWORD dwOwnerID, DWORD dwItemID, bool updateQuery) { // searching for shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching for item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // not found // skipping unlocked items if(!it->second->lock) return false; // removing item from cache itemsmap.erase(it); // updating ikashop_data if (updateQuery) { std::string query = CreateShopCacheDeleteItemQuery(dwOwnerID, dwItemID); CDBManager::instance().DirectQuery(query.c_str()); } return true; } bool CShopCache::LockSellItem(DWORD dwOwnerID, DWORD dwItemID, long long TotalPriceSeen) //patch seen price check { // searching shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // not found // skipping locked items if (it->second->lock) return false; // skipping items with a changed price if (it->second->price.GetTotalYangAmount() != TotalPriceSeen) return false; // locking item it->second->lock = true; return true; } bool CShopCache::UnlockSellItem(DWORD dwOwnerID, DWORD dwItemID)//topatch { // searching shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // not found // skipping unlocked items if (!it->second->lock) return false; // unlocking item it->second->lock = false; return true; } bool CShopCache::EditItem(DWORD dwOwnerID, DWORD dwItemID, const TPriceInfo& price) { // searching shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // skipping locked items if(it->second->lock) return false; // updating price on cache it->second->price = price; // updating price on database std::string query = CreateShopCacheUpdateItemQuery(*it->second); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_EDIT_ITEM, 0, NULL); return true; } bool CShopCache::CloseShop(DWORD dwOwnerID) { // searching shop cache auto it = m_shopsMap.find(dwOwnerID); if (it == m_shopsMap.end()) return false; // removing shop cache m_shopsMap.erase(it); // removing shop from db const auto query = CreateShopCacheDeleteShopQuery(dwOwnerID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_DELETE_SHOP, 0, NULL); return true; } #ifdef ENABLE_IKASHOP_ENTITIES bool CShopCache::CreateShop(DWORD dwOwnerID, DWORD dwDuration, const char* szName, const TShopSpawn& spawn) #else bool CShopCache::CreateShop(DWORD dwOwnerID, DWORD dwDuration, const char* szName) #endif { // searching if shop already exists auto cache = Get(dwOwnerID); if(cache) return false; // it already exists // making query create shop info auto* qi = new SQueryInfoCreateShop; qi->owner = dwOwnerID; qi->duration = dwDuration; qi->name = szName; #ifdef ENABLE_IKASHOP_ENTITIES qi->spawn = spawn; #endif // sending query std::string query = CreateShopCacheInsertShopQuery(dwOwnerID, dwDuration, szName #ifdef ENABLE_IKASHOP_ENTITIES , spawn #endif ); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_CREATE_SHOP, 0, qi); return true; } bool CShopCache::ChangeShopName(DWORD dwOwnerID, const char* szName) { // searching shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // updating cached name cache->name = szName; return true; } bool CShopCache::PutItem(DWORD dwOwnerID, DWORD dwItemID, const TShopCacheItemInfo& item) { // searching shop cache auto cache = Get(dwOwnerID); if(!cache) return false; // checking item already exists auto& itemsmap = cache->itemsmap; if(itemsmap.contains(dwItemID)) return false; // already exists // inserting the item itemsmap[dwItemID] = std::make_shared(item); return true; } bool CShopCache::PutShop(DWORD dwOwnerID, DWORD dwDuration, const char* szName #ifdef ENABLE_IKASHOP_ENTITIES , const TShopSpawn& spawn #endif #ifdef EXTEND_IKASHOP_ULTIMATE , int decoration, int decoration_time, int lock_index #endif ) { // searching shop cache already exists if(m_shopsMap.contains(dwOwnerID)) return false; auto& shop = m_shopsMap[dwOwnerID] = std::make_shared(); shop->duration = dwDuration; shop->name = szName; #ifdef ENABLE_IKASHOP_ENTITIES shop->spawn = spawn; #endif #ifdef EXTEND_IKASHOP_ULTIMATE shop->decoration = decoration; shop->decoration_time = decoration_time; shop->lock_index = lock_index; #endif return true; } void CShopCache::EncodeCache(CPeer* peer) const { for(auto& [owner, shop] : m_shopsMap) { static TShopInfo pack{}; pack.duration = shop->duration; pack.ownerid = owner; pack.count = shop->itemsmap.size(); str_to_cstring(pack.name, shop->name.c_str()); #ifdef ENABLE_IKASHOP_ENTITIES pack.spawn = shop->spawn; #endif #ifdef EXTEND_IKASHOP_ULTIMATE pack.decoration = shop->decoration; pack.decoration_time = shop->decoration_time; pack.lock_index = shop->lock_index; #endif peer->Encode(&pack, sizeof(pack)); for (auto& [id, item] : shop->itemsmap) peer->Encode(static_cast(item.get()), sizeof(TShopItem)); } } DWORD CShopCache::GetItemCount() const { return std::accumulate(m_shopsMap.begin(), m_shopsMap.end(), 0UL, [](const auto& val, const auto& iter){ return val + iter.second->itemsmap.size(); }); } void CShopCache::ShopDurationProcess() { //expired check std::set expiredShops; for (auto& [owner, shop] : m_shopsMap) { // checking for expiring shop if(shop->duration == 1) expiredShops.emplace(owner); if (shop->duration != 0 && --shop->duration != 0 && shop->duration % 5 == 0) UpdateDurationQuery(owner, *shop); #ifdef EXTEND_IKASHOP_ULTIMATE if (shop->decoration_time != 0 && --shop->decoration_time != 0 && shop->decoration_time % 5 == 0) UpdateDecorationTime(owner, shop->decoration_time); if (shop->decoration != 0 && shop->decoration_time == 0) { // updating cache shop->decoration = 0; // updating table const auto query = fmt::format("UPDATE `ikashop_offlineshop` SET `decoration` = 0, `decoration_time` = 0 WHERE `owner` = {}", owner); CDBManager::instance().DirectQuery(query.c_str()); // updating cores CClientManager::instance().SendIkarusShopDecorationSet(owner, 0, 0); CClientManager::instance().AppendIkarusShopNotification(ikashop::ENotificationType::SELLER_DECORATION_EXPIRED, owner, 0, "", ""); #ifdef ENABLE_IKASHOP_LOGS CClientManager::instance().IkarusShopLog(owner, 0, "SHOP_DECORATION_EXPIRED", owner); #endif } #endif } //item expired check std::vector> item_vec; const time_t now = std::time(nullptr); for (auto& [owner, shop] : m_shopsMap) { // skipping expired shops if(expiredShops.contains(owner)) continue; // searching for expired items for(auto& [id, item] : shop->itemsmap) if(IsItemExpired(now, *item)) item_vec.emplace_back(std::make_pair(owner, id)); } // removing expired shops for (auto owner : expiredShops){ #ifdef ENABLE_IKASHOP_LOGS CClientManager::instance().IkarusShopLog(owner, 0, "SHOP_DURATION_EXPIRED", owner); #endif CClientManager::instance().IkarusShopExpiredShop(owner); } // removing expired items for(auto& [owner,id] : item_vec){ #ifdef ENABLE_IKASHOP_LOGS auto item = GetItem(owner, id); CClientManager::instance().IkarusShopLog(owner, id, "SHOP_ITEM_EXPIRED", owner, "", item->vnum, item->count); #endif CClientManager::Instance().SendIkarusShopRemoveItemPacket(owner, id); CClientManager::Instance().RemoveOfferOnShopItem(owner, id); DeleteItem(owner, id); } } void CShopCache::UpdateDurationQuery(DWORD dwOwnerID, const TShopCacheInfo& rShop) { std::string query = CreateShopCacheUpdateDurationQuery(dwOwnerID, rShop.duration); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SHOP_UPDATE_DURATION, 0 , NULL); } CShopCache::ITEM_CACHE_HANDLE CShopCache::GetItem(DWORD owner, DWORD item) { if(auto shop = Get(owner)) if(auto iter = shop->itemsmap.find(item); iter != shop->itemsmap.end()) return iter->second; return nullptr; } #ifdef ENABLE_IKASHOP_ENTITIES #ifdef EXTEND_IKASHOP_PRO bool CShopCache::MoveShopEntity(DWORD owner, const TShopSpawn& spawn) { auto shop = Get(owner); if(!shop) return false; // update table auto query = fmt::format("UPDATE `ikashop_offlineshop` SET `map` = {}, `x` = {}, `y` = {} WHERE `owner` = {}", spawn.map, spawn.x, spawn.y, owner); CDBManager::instance().AsyncQuery(query.c_str()); // update cache shop->spawn = spawn; return true; } #endif #endif #ifdef EXTEND_IKASHOP_ULTIMATE bool CShopCache::ChangeDecoration(DWORD owner, int index) { auto shop = Get(owner); if (!shop) return false; shop->decoration = index; shop->decoration_time = OFFSHOP_DECORATION_TIME; return true; } void CShopCache::UpdateDecorationTime(DWORD owner, int time) { const auto& query = fmt::format("UPDATE `ikashop_offlineshop` SET `decoration_time` = {} WHERE `owner` = {}", time, owner); CDBManager::instance().AsyncQuery(query.c_str()); } bool CShopCache::MoveItem(DWORD owner, DWORD itemid, int destCell) { auto item = GetItem(owner, itemid); if (!item || item->lock) return false; // updating cache item->pos = destCell; // updating table const auto query = fmt::format("UPDATE `item` SET `pos` = {} WHERE `id` = {} AND `owner_id` = {} AND `window` = 'IKASHOP_OFFLINESHOP'", destCell, itemid, owner); CDBManager::instance().AsyncQuery(query.c_str()); return true; } #endif //SAFEBOX CHACHE CSafeboxCache::CSafeboxCache() { } CSafeboxCache::~CSafeboxCache() { } CSafeboxCache::SAFEBOX_HANDLE CSafeboxCache::Get(DWORD dwOwnerID) const { auto it = m_safeboxMap.find(dwOwnerID); if(it == m_safeboxMap.end()) return nullptr; return it->second; } bool CSafeboxCache::PutSafebox(DWORD dwOwnerID, const TSafeboxCacheInfo& safebox) { // searching for duplicates if(m_safeboxMap.contains(dwOwnerID)) return false; // inserting into map m_safeboxMap[dwOwnerID] = std::make_shared(safebox); return true; } bool CSafeboxCache::PutItem(DWORD dwOwnerID, DWORD dwItem, const TShopPlayerItem& item) { // searching safebox cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching safebox item cache auto& itemsmap = cache->itemsmap; if(itemsmap.contains(dwItem)) return false; // inserting into map itemsmap[dwItem] = item; return true; } bool CSafeboxCache::DeleteItem(DWORD dwOwnerID, DWORD dwItemID) { // searching safebox cache auto cache = Get(dwOwnerID); if(!cache) return false; // searching safebox item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // removing item from cache itemsmap.erase(it); // removing item from table std::string query = CreateSafeboxCacheDeleteItemQuery(dwItemID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_DELETE_ITEM, 0, NULL); return true; } bool CSafeboxCache::LockItem(DWORD dwOwner, DWORD dwItemID) { // searching safebox cache auto cache = Get(dwOwner); if (!cache) return false; // searching safebox item cache auto& itemsmap = cache->itemsmap; auto it = itemsmap.find(dwItemID); if (it == itemsmap.end()) return false; // adding item to locked map auto& lockmap = cache->lockedmap; lockmap[dwItemID] = it->second; // removing item from cache itemsmap.erase(it); return true; } bool CSafeboxCache::RemoveLockedItem(DWORD dwOwner, DWORD dwItemID) { // searching safebox cache auto cache = Get(dwOwner); if (!cache) return false; // searching into lockmap auto& lockmap = cache->lockedmap; auto lockiter = lockmap.find(dwItemID); if(lockiter == lockmap.end()) return false; // removing from map lockmap.erase(lockiter); // blanking ikashop_data on db auto query = fmt::format("UPDATE `item` SET `ikashop_data` = '' WHERE `id` = {}", dwItemID); CDBManager::instance().DirectQuery(query.c_str()); return true; } bool CSafeboxCache::UnlockItem(DWORD dwOwner, DWORD dwItemID) { // searching safebox cache auto cache = Get(dwOwner); if (!cache) return false; // searching locked item auto& lockmap = cache->lockedmap; auto lockiter = lockmap.find(dwItemID); if(lockiter == lockmap.end()) return false; // adding item to itemsmap auto& itemsmap = cache->itemsmap; itemsmap[dwItemID] = lockiter->second; // removing from locked items lockmap.erase(lockiter); return true; } bool CSafeboxCache::AddItem(DWORD dwOwnerID, DWORD dwItemID, const TShopPlayerItem& item) { // saving item into query info auto queryItem = new SQueryInfoSafeboxAddItem(item); queryItem->owner = dwOwnerID; // updating owner // pushing query std::string query = CreateSafeboxCacheInsertItemQuery(dwOwnerID, dwItemID, item); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_ADD_ITEM, 0, queryItem); query = fmt::format("UPDATE `item` SET `window` = 'IKASHOP_SAFEBOX', `owner_id` = {} WHERE `id` = {} ", dwOwnerID, dwItemID); CDBManager::Instance().DirectQuery(query.c_str()); return true; } bool CSafeboxCache::AddValutes(DWORD dwOwnerID, const TValutesInfo& val) { // searching safebox cache auto cache = Get(dwOwnerID); if(!cache) { // adding as query -> it wasn't loaded yet on cache AddValutesAsQuery(dwOwnerID, val); return true; } // updating values on cache cache->valutes += val; // updating values into db std::string query = CreateSafeboxCacheUpdateValutes(dwOwnerID, cache->valutes); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_UPDATE_VALUTES, 0, NULL); return true; } void CSafeboxCache::AddValutesAsQuery(DWORD dwOwnerID, const TValutesInfo& val) { std::string query=CreateSafeboxCacheUpdateValutesByAdding(dwOwnerID,val); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_UPDATE_VALUTES_ADDING, 0, NULL); } bool CSafeboxCache::RemoveValutes(DWORD dwOwnerID, const TValutesInfo& val) { // searching cache auto cache = Get(dwOwnerID); if(!cache) return false; // updating values into cache cache->valutes -= val; // updating values into db std::string query = CreateSafeboxCacheUpdateValutes(dwOwnerID, cache->valutes); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_UPDATE_VALUTES, 0, NULL); return true; } CSafeboxCache::SAFEBOX_HANDLE CSafeboxCache::CreateSafebox(DWORD dwOwnerID) { // inserting into db an empty safebox std::string query = CreateSafeboxCacheInsertSafeboxValutesQuery(dwOwnerID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_SAFEBOX_INSERT_VALUTES, 0, NULL); // inserting into cache return (m_safeboxMap[dwOwnerID] = std::make_shared()); } CSafeboxCache::SAFEBOX_HANDLE CSafeboxCache::LoadSafebox(DWORD dwPID) { // searching inside cache auto cache = Get(dwPID); if(cache) return cache; // making query const auto query = CreateSafeboxCacheLoadValutesQuery(dwPID); auto msg = CDBManager::instance().DirectQuery(query.c_str()); // checking for errors if(msg->uiSQLErrno != 0) return nullptr; // no row so creating it from 0 if (msg->Get()->uiAffectedRows == 0) return CreateSafebox(dwPID); // inserting new empty cache auto& safebox = m_safeboxMap[dwPID] = std::make_shared(); // extracting data auto row = mysql_fetch_row(msg->Get()->pSQLResult); str_to_number(safebox->valutes.yang, row[0]); // making query selecting items const auto itemquery = CreateSafeboxCacheLoadItemsQuery(dwPID); msg = CDBManager::instance().DirectQuery(itemquery.c_str()); // extracting items auto& itemsmap = safebox->itemsmap; auto item = TShopPlayerItem{}; while ((row = mysql_fetch_row(msg->Get()->pSQLResult))) { int col = 0; str_to_number(item.id, row[col++]); str_to_number(item.vnum, row[col++]); str_to_number(item.count, row[col++]); for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++) str_to_number(item.alSockets[i], row[col++]); for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++) { str_to_number(item.aAttr[i].bType, row[col++]); str_to_number(item.aAttr[i].sValue, row[col++]); } #ifdef ENABLE_CHANGELOOK_SYSTEM str_to_number(item.dwTransmutation, row[col++]); #endif auto result = str_to_json(row[col++]); if(!result){ sys_err("CANNOT EXTRACT JSON FROM SHOP SAFEBOX ITEM : pid(%u) item_id(%u)", dwPID, item.id); continue; } auto expirationResult = jsonHelper::getValue(result.value(), "expiration"); if(!expirationResult){ sys_err("CANNOT EXTRACT JSON EXPIRATION FROM SHOP SAFEBOX ITEM : pid(%u) item_id(%u)", dwPID, item.id); continue; } item.owner = dwPID; item.expiration = static_cast(expirationResult.value()); itemsmap[item.id] = item; } return safebox; } DWORD CSafeboxCache::GetItemCount() const { return std::accumulate(m_safeboxMap.begin(), m_safeboxMap.end(), 0UL, [](const auto& value, const auto& iter){ return value + iter.second->itemsmap.size(); }); } //patch 08-03-2020 void CSafeboxCache::ItemExpirationProcess() { // making temp vector std::vector> vec; const auto now = std::time(nullptr); // iterating checking item expiration for (auto& [owner, safebox] : m_safeboxMap){ for (auto& [id,item] : safebox->itemsmap) { if(IsItemExpired(now, item)){ #ifdef ENABLE_IKASHOP_LOGS CClientManager::instance().IkarusShopLog(owner, id, "SAFEBOX_ITEM_EXPIRED", owner, "", item.vnum, item.count); #endif vec.emplace_back(std::make_pair(owner, id)); } } } // removing expired items for (const auto& [owner, id] : vec) { DeleteItem(owner, id); CClientManager::instance().SendIkarusShopSafeboxExpiredItem(owner, id); } } //OFFER CACHE COfferCache::COfferCache() { } COfferCache::~COfferCache() { } bool COfferCache::Puts(const TOfferInfo& offer) { // checking for duplicates if(m_mapOffersByBuyer.contains(offer.offerid)) return false; // inserting into offers cache auto& handle = m_mapOffersByBuyer[offer.offerid] = std::make_shared(offer); // inserting into offers by shop auto& vec = m_findOffersByShop[offer.ownerid]; vec.emplace_back(handle); return true; } COfferCache::OFFER_HANDLE COfferCache::Get(DWORD dwOfferID) const { auto it = m_mapOffersByBuyer.find(dwOfferID); if(it == m_mapOffersByBuyer.end()) return nullptr; return it->second; } bool COfferCache::AddOffer(const TOfferInfo& offer) { const auto query = CreateOfferCacheInsertOfferQuery(offer); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_OFFER_ADD, 0, new SQueryInfoOfferAdd(offer)); return true; } bool COfferCache::NotifiedOffer(DWORD dwOfferID) { // searching offer cache auto cache = Get(dwOfferID); if(!cache) return false; // updating value into cache cache->notified = true; // updating value into db std::string query = CreateOfferCacheUpdateNotifiedQuery(dwOfferID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_OFFER_UPDATE_NOTIFIED, 0, NULL); return true; } bool COfferCache::CancelOffer(DWORD dwOfferID, DWORD dwOwnerID) //ikashop-updated 04/08/19 reminder { // searching offer cache auto cache = Get(dwOfferID); if(!cache) return false; // searching offer into shop offers vector auto it = m_findOffersByShop.find(dwOwnerID); // removing from vector if(it != m_findOffersByShop.end()) std::erase(it->second, cache); // checking if vector is empty if(it->second.empty()) m_findOffersByShop.erase(it); // removing offer from map m_mapOffersByBuyer.erase(dwOfferID); // removing offer from db std::string query = CreateOfferCacheDeleteOfferQuery(dwOfferID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_OFFER_DELETE, 0, NULL); return true; } bool COfferCache::AcceptOffer(DWORD dwOfferID) { // searching offer cache auto cache = Get(dwOfferID); if (!cache) return false; return CancelOffer(dwOfferID, cache->ownerid); } bool COfferCache::RemoveOffersByShopOwner(DWORD dwShopOwnerID) { // searching offer vector auto it = m_findOffersByShop.find(dwShopOwnerID); if(it == m_findOffersByShop.end()) return false; // removing all offers from cache for(auto& handle : it->second) m_mapOffersByBuyer.erase(handle->offerid); // removing vector from cache m_findOffersByShop.erase(it); // removing all offers from db std::string query = CreateOfferCacheRemoveOfferByShopOwner(dwShopOwnerID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_OFFER_DELETE_BY_SHOP, 0, NULL); return true; } void COfferCache::EncodeCache(CPeer* peer) const { for(auto& [id, offer] : m_mapOffersByBuyer) peer->Encode(offer.get(), sizeof(*offer)); } std::vector COfferCache::GetOffersByShopOwner(DWORD dwOwnerID) const { auto it = m_findOffersByShop.find(dwOwnerID); if(it == m_findOffersByShop.end()) return {}; return it->second; } std::vector COfferCache::GetOffersByItemID(DWORD dwOwnerID, DWORD dwItemID) const { // searching offers vector auto it = m_findOffersByShop.find(dwOwnerID); if (it == m_findOffersByShop.end()) return {}; // making filter lambda auto filterFunc = [&](auto&& handle) { return handle->itemid == dwItemID; }; // making filtered vector std::vector ret; std::copy_if(it->second.begin(), it->second.end(), std::back_inserter(ret), filterFunc); return ret; } //AUCTION CAuctionCache::CAuctionCache() { } CAuctionCache::~CAuctionCache() { } bool CAuctionCache::PutsAuction(const TAuctionInfo& auction) { auto handle = std::make_shared(auction); m_mapAuction[auction.owner] = handle; m_mapAuctionOffer[auction.id] = {}; return true; } bool CAuctionCache::PutsAuctionOffer(const TAuctionOfferInfo& auctionOffer) { auto& vec = m_mapAuctionOffer[auctionOffer.ownerid]; vec.emplace_back(std::make_shared(auctionOffer)); return true; } bool CAuctionCache::AddAuction(const TAuctionInfo& auction) { std::string query = CreateAuctionCacheAddAuctionQuery(auction); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_AUCTION_INSERT, 0, NULL); PutsAuction(auction); return true; } bool CAuctionCache::AddOffer(const TAuctionOfferInfo& auctionOffer, bool quering) { if (quering) { std::string query = CreateAuctionCacheAddOfferQuery(auctionOffer); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_AUCTION_INSERT_OFFER, 0 , NULL); //adding 1 minute time when new offer is done in the last minute + if(auto cache = Get(auctionOffer.ownerid)) if(cache->duration == 0) cache->duration = 1; } PutsAuctionOffer(auctionOffer); return true; } bool CAuctionCache::ExpiredAuction(DWORD dwOwnerID) { // removing from cache m_mapAuction.erase(dwOwnerID); m_mapAuctionOffer.erase(dwOwnerID); // - disabling since ikashop_data will be update by adding it on shop safebox //CDBManager::instance().ReturnQuery(CreateAuctionCacheDeleteAuction(dwOwnerID).c_str(), QID_OFFLINESHOP_AUCTION_DELETE, 0, NULL); // removing from db const auto query = CreateAuctionCacheDeleteAuctionOffers(dwOwnerID); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_AUCTION_DELETE_OFFERS, 0 , NULL); return true; } //patch 08-03-2020 bool CAuctionCache::AuctionDurationProcess() { if(m_mapAuction.empty()) return false; std::vector expired_auctions; std::vector expired_items; const time_t now = time(0); for(auto&[owner, auction] : m_mapAuction) { //check about the item expiration if(IsItemExpired(now, *auction)) expired_items.emplace_back(owner); // check about auction expiration else if (auction->duration <= 1) expired_auctions.emplace_back(owner); // check about duration update on db else if(--auction->duration % 5 == 0) _UpdateDuration(*auction); } // expired auctions for(auto& id : expired_auctions) CClientManager::instance().IkarusShopExpiredAuction(id); // expired auction items for (auto& id : expired_items) CClientManager::instance().IkarusShopExpiredAuctionItem(id); return true; } void CAuctionCache::_UpdateDuration(const TAuctionInfo & auction) { std::string query = CreateAuctionCacheUpdateDurationQuery(auction); CDBManager::instance().ReturnQuery(query.c_str(), QID_OFFLINESHOP_AUCTION_UPDATE_DURATION, 0 , NULL); } CAuctionCache::AUCTION_OFFER_HANDLE CAuctionCache::GetBestBuyerOffer(DWORD dwOwnerID) { // searching vector auto it = m_mapAuctionOffer.find(dwOwnerID); if(it == m_mapAuctionOffer.end() || it->second.empty()) return nullptr; // obtaining max element auto& offerlist = it->second; auto itbest = std::max_element(offerlist.begin(), offerlist.end(), [](auto&& first, auto&& second) { return first->price < second->price; }); return itbest != offerlist.end() ? *itbest : nullptr; } CAuctionCache::AUCTION_HANDLE CAuctionCache::Get(DWORD dwOwnerID) { auto it = m_mapAuction.find(dwOwnerID); if(it == m_mapAuction.end()) return nullptr; return it->second; } std::vector CAuctionCache::GetOffers(DWORD dwOwnerID) { auto it = m_mapAuctionOffer.find(dwOwnerID); if(it == m_mapAuctionOffer.end()) return {}; return it->second; } DWORD CAuctionCache::GetCount() { return m_mapAuction.size(); } DWORD CAuctionCache::GetOffersCount() { DWORD dwCount =0; for(itertype(m_mapAuctionOffer) it = m_mapAuctionOffer.begin(); it != m_mapAuctionOffer.end(); it++) dwCount += it->second.size(); return dwCount; } void CAuctionCache::EncodeCache(CPeer* peer) { for(auto& [id, auction] : m_mapAuction) peer->Encode(auction.get(), sizeof(*auction)); for(auto& [id, offers] : m_mapAuctionOffer) for(auto& offer : offers) peer->Encode(offer.get(), sizeof(*offer)); } #ifdef EXTEND_IKASHOP_ULTIMATE bool CAveragePriceCache::InitializeTable() { auto msg = CDBManager::instance().DirectQuery("SELECT `id`, `vnum`, `count`, `price`, UNIX_TIMESTAMP(`datetime`) FROM `ikashop_sale`"); if(!msg || !msg->Get() || msg->uiSQLErrno != 0) return false; const auto currentTime = std::time(nullptr); m_priceAverageVector.reserve(msg->Get()->uiNumRows); for(uint32_t i=0; i < msg->Get()->uiNumRows; i++) { if(auto row = mysql_fetch_row(msg->Get()->pSQLResult)) { int col = 0; TSaleHistory history{}; str_to_number(history.account, row[col++]); str_to_number(history.vnum, row[col++]); str_to_number(history.count, row[col++]); str_to_number(history.price, row[col++]); str_to_number(history.datetime, row[col++]); if(history.datetime + OFFLINESHOP_EXPIRING_SALE_HISTORY < currentTime) continue; m_priceAverageVector.emplace_back(history); } } auto deleteQuery = fmt::format("DELETE FROM `ikashop_sale` WHERE UNIX_TIMESTAMP(`datetime`) < {}", currentTime - OFFLINESHOP_EXPIRING_SALE_HISTORY); CDBManager::instance().AsyncQuery(deleteQuery.c_str()); return true; } bool CAveragePriceCache::RegisterSelling(const TSaleHistory& data) { auto& vnumMap = m_cooldownMap[data.account]; auto& timestamp = vnumMap[data.vnum]; const auto currentTime = std::time(nullptr); if(timestamp + OFFLINESHOP_SALE_HISTORY_COOLDOWN > currentTime) return false; timestamp = currentTime; // adding to cache m_priceAverageVector.emplace_back(data); // adding to table const auto query = fmt::format("INSERT INTO `ikashop_sale` (`id`, `vnum`, `count`, `price`) VALUES({}, {}, {}, {})", data.account, data.vnum, data.count, data.price); CDBManager::instance().AsyncQuery(query.c_str()); return true; } void CAveragePriceCache::Encode(CPeer* peer) { peer->EncodeDWORD(m_priceAverageVector.size()); if(!m_priceAverageVector.empty()) peer->Encode(m_priceAverageVector.data(), sizeof(TSaleHistory)* m_priceAverageVector.size()); } #endif // QUERY MAKE std::string CreateShopCacheUpdateItemQuery(const TShopItem& item) { // making json object rapidjson::Document value; value.SetObject(); // inserting values auto& allocator = value.GetAllocator(); jsonHelper::insertValue(value, "yang", item.price.yang, allocator); #ifdef ENABLE_CHEQUE_SYSTEM jsonHelper::insertValue(value, "cheque", item.price.cheque, allocator); #endif jsonHelper::insertValue(value, "expiration", static_cast(item.expiration), allocator); // making json string rapidjson::StringBuffer buffer; buffer.Clear(); rapidjson::Writer StringWriter(buffer); value.Accept(StringWriter); // escaping json string static char escape[1024]; CDBManager::instance().EscapeString(escape, buffer.GetString(), buffer.GetSize()); // making query return fmt::format("UPDATE `item` SET `ikashop_data`='{}' WHERE `id`={}", escape, item.id); } std::string CreateShopCacheDeleteShopQuery(DWORD dwOwner) { return fmt::format("DELETE FROM `ikashop_offlineshop` WHERE `owner` = {}", dwOwner); } std::string CreateShopCacheDeleteShopItemQuery(DWORD dwOwner) { return fmt::format("UPDATE `item` SET `ikashop_data` = '' WHERE `window` = 'IKASHOP_OFFLINESHOP' AND `owner_id` = {}", dwOwner); } #ifdef ENABLE_IKASHOP_ENTITIES std::string CreateShopCacheInsertShopQuery(DWORD dwOwnerID, DWORD dwDuration, const char* name, const TShopSpawn& spawn) #else std::string CreateShopCacheInsertShopQuery(DWORD dwOwnerID, DWORD dwDuration, const char* name) #endif { return fmt::format("INSERT INTO `ikashop_offlineshop` (`owner`, `duration`" #ifdef ENABLE_IKASHOP_ENTITIES ", `map`, `x`, `y`" #endif ") VALUES({}, {}" #ifdef ENABLE_IKASHOP_ENTITIES ", {}, {}, {}" #endif ");", dwOwnerID, dwDuration #ifdef ENABLE_IKASHOP_ENTITIES , spawn.map, spawn.x, spawn.y #endif ); } std::string CreateShopCacheUpdateDurationQuery(DWORD dwOwnerID, DWORD dwDuration) { return fmt::format("UPDATE `ikashop_offlineshop` SET `duration` = '{}' WHERE `owner` = {};", dwDuration, dwOwnerID); } std::string CreateSafeboxCacheDeleteItemQuery(DWORD dwItem) { return fmt::format("DELETE FROM `item` WHERE `id` = {}", dwItem); } std::string CreateSafeboxCacheInsertItemQuery(DWORD dwOwner, DWORD dwItemID, const TShopPlayerItem& item) { // making json value rapidjson::Document jsonValue; jsonValue.SetObject(); // encoding data auto& allocator = jsonValue.GetAllocator(); jsonHelper::insertValue(jsonValue, "expiration", static_cast(item.expiration), allocator); // making json string rapidjson::StringBuffer buffer; buffer.Clear(); rapidjson::Writer StringWriter(buffer); jsonValue.Accept(StringWriter); // escaping json string static char escape[1024]; CDBManager::instance().EscapeString(escape, buffer.GetString(), buffer.GetSize()); return fmt::format("UPDATE `item` SET `ikashop_data` = '{}' WHERE `id` = {}", escape, item.id); } std::string CreateSafeboxCacheUpdateValutes(DWORD dwOwner, const TValutesInfo& val) { return fmt::format("UPDATE `ikashop_safebox` SET `gold` = {} WHERE `owner` = {};", val.yang, dwOwner); } std::string CreateSafeboxCacheUpdateValutesByAdding(DWORD dwOwner, const TValutesInfo& val) { return fmt::format("UPDATE `ikashop_safebox` SET `gold` = `gold`+{} WHERE `owner` = {};", val.yang, dwOwner); } std::string CreateSafeboxCacheInsertSafeboxValutesQuery(DWORD dwOwnerID) { return fmt::format("INSERT INTO `ikashop_safebox` (`owner`,`gold`,`cheque`) VALUES({} , 0,0);", dwOwnerID); } std::string CreateSafeboxCacheLoadValutesQuery(DWORD dwOwnerID) { return fmt::format("SELECT `gold` from `ikashop_safebox` WHERE `owner` = {};", dwOwnerID); } std::string CreateSafeboxCacheLoadItemsQuery(DWORD dwOwnerID) { std::string query = "SELECT `id`, `vnum`, `count` "; for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++) query += fmt::format(",`socket{}` ", i); for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++) query += fmt::format(",`attrtype{}` , `attrvalue{}` ", i, i); #ifdef ENABLE_CHANGELOOK_SYSTEM query += " , `trans` "; #endif query += fmt::format(", `ikashop_data` FROM `item` WHERE `owner_id` = {} AND `window` = 'IKASHOP_SAFEBOX'", dwOwnerID); return query; } //OFFERCHACHE std::string CreateOfferCacheInsertOfferQuery(const TOfferInfo& rOffer) { //ikashop-updated 03/08/19 static char szEscapeString[CHARACTER_NAME_MAX_LEN + 32]; CDBManager::instance().EscapeString(szEscapeString, rOffer.buyername, strnlen(rOffer.buyername, sizeof(rOffer.buyername))); // offer_id,owner_id,offerer_id,price_yang,price_cheque,is_notified,is_accept return fmt::format( "INSERT INTO `ikashop_private_offer` (`id`,`seller`,`buyer`, `item`, `price`, `notified`,`accepted`, `buyer_name`) VALUES (0, {}, {}, {}, {}, {}, {}, '{}')", rOffer.ownerid, rOffer.buyerid, rOffer.itemid, rOffer.price.yang, rOffer.notified?1:0, rOffer.accepted?1:0, szEscapeString); } std::string CreateOfferCacheUpdateNotifiedQuery(DWORD dwOfferID) { return fmt::format("UPDATE `ikashop_private_offer` SET `notified` = 1 WHERE `buyer` = {};", dwOfferID); } std::string CreateOfferCacheUpdateAcceptedQuery(DWORD dwOfferID) { return fmt::format("DELETE FROM `ikashop_private_offer` WHERE `id` = {};", dwOfferID); } std::string CreateOfferCacheRemoveOfferByShopOwner(DWORD dwShopOwner) { return fmt::format("DELETE from `ikashop_private_offer` WHERE `seller` = {};", dwShopOwner); } std::string CreateOfferCacheDeleteOfferQuery(DWORD dwOfferID) { return fmt::format("DELETE from `ikashop_private_offer` WHERE `id` = {};", dwOfferID); } std::string CreateShopCacheDeleteItemQuery(DWORD dwOwnerID, DWORD dwItemID) { return fmt::format("UPDATE `item` SET `ikashop_data` = '' WHERE `id` = {}", dwItemID); } std::string CreateAuctionCacheAddAuctionQuery(const TAuctionInfo& auction) { // making json object rapidjson::Document jsonValue; jsonValue.SetObject(); auto& allocator = jsonValue.GetAllocator(); // encoding values jsonHelper::insertValue(jsonValue, "expiration", static_cast(auction.expiration), allocator); jsonHelper::insertValue(jsonValue, "duration", static_cast(auction.duration), allocator); jsonHelper::insertValue(jsonValue, "price", static_cast(auction.price.yang), allocator); #ifdef ENABLE_IKASHOP_ENTITIES #ifdef EXTEND_IKASHOP_ULTIMATE jsonHelper::insertValue(jsonValue, "map", auction.spawn.map, allocator); jsonHelper::insertValue(jsonValue, "x", auction.spawn.x, allocator); jsonHelper::insertValue(jsonValue, "y", auction.spawn.y, allocator); #endif #endif // constructing json string rapidjson::StringBuffer buffer; buffer.Clear(); rapidjson::Writer StringWriter(buffer); jsonValue.Accept(StringWriter); // escaping json string static char escape[500]; CDBManager::instance().EscapeString(escape, buffer.GetString(), buffer.GetSize()); // making query return fmt::format("UPDATE `item` SET `ikashop_data` = '{}' WHERE `id` = {}", escape, auction.id); } std::string CreateAuctionCacheAddOfferQuery(const TAuctionOfferInfo& auctionOffer) { static char szEscapeName[CHARACTER_NAME_MAX_LEN+32]; CDBManager::instance().EscapeString(szEscapeName, auctionOffer.buyername, strnlen(auctionOffer.buyername , sizeof(auctionOffer.buyername))); return fmt::format("INSERT INTO `ikashop_auction_offer` (`seller`, `buyer`, `buyer_name` , `price`, `seen`) " "VALUES( {}, {}, '{}', {}, 0 );", auctionOffer.ownerid, auctionOffer.buyerid, szEscapeName, auctionOffer.price.yang); } std::string CreateAuctionCacheDeleteAuctionOffers(DWORD dwOwnerID) { return fmt::format("DELETE FROM `ikashop_auction_offer` WHERE `seller` = {};", dwOwnerID); } std::string CreateAuctionCacheUpdateDurationQuery(const TAuctionInfo& info) { return CreateAuctionCacheAddAuctionQuery(info); } } #endif