Marty-v58/source-server/Srcs/Server/db/src/IkarusShopCache.cpp
2024-10-25 00:14:42 -05:00

1404 lines
40 KiB
C++

#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<class T>
bool IsItemExpired(const auto now, const T& item){
switch (static_cast<ikashop::ExpirationType>(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<TShopCacheItemInfo>(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<TShopCacheInfo>();
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<TShopItem*>(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<DWORD> 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<std::pair<DWORD, DWORD>> 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<TSafeboxCacheInfo>(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<TSafeboxCacheInfo>());
}
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<TSafeboxCacheInfo>();
// 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<int>(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<ikashop::ExpirationType>(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<std::pair<DWORD, DWORD>> 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<TOfferInfo>(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::OFFER_HANDLE> COfferCache::GetOffersByShopOwner(DWORD dwOwnerID) const
{
auto it = m_findOffersByShop.find(dwOwnerID);
if(it == m_findOffersByShop.end())
return {};
return it->second;
}
std::vector<COfferCache::OFFER_HANDLE> 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<OFFER_HANDLE> 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<TAuctionInfo>(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<TAuctionOfferInfo>(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<DWORD> expired_auctions;
std::vector<DWORD> 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::AUCTION_OFFER_HANDLE> 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<int>(item.expiration), allocator);
// making json string
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> 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<int>(item.expiration), allocator);
// making json string
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> 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<int>(auction.expiration), allocator);
jsonHelper::insertValue(jsonValue, "duration", static_cast<int>(auction.duration), allocator);
jsonHelper::insertValue(jsonValue, "price", static_cast<int64_t>(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<rapidjson::StringBuffer> 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