DB

1. ClientManager.cpp

Search:

	CClientManager::CClientManager() :
		m_pkAuthPeer(NULL),
		m_iPlayerIDStart(0),
		m_iPlayerDeleteLevelLimit(0),
		m_iPlayerDeleteLevelLimitLower(0),
		m_iShopTableSize(0),
		m_pShopTable(NULL),
		m_iRefineTableSize(0),
		m_pRefineTable(NULL),

Add:

	#ifdef ENABLE_ITEMSHOP
		m_iItemshopTableCategorySize(0),
		m_pItemshopTableCategories(NULL),
		m_iItemshopTableItemSize(0),
		m_pItemshopTableItems(NULL),
	#endif

Search:

		memset(g_query_count, 0, sizeof(g_query_count));

Add:

	#ifdef ENABLE_ITEMSHOP
		m_map_ItemshopLimitCountFlush.clear();
		m_vec_usedSeeds.clear();
		m_ItemshopItems.clear();
	#endif

Search:

		if (!InitializeTables())
		{
			sys_err("Table Initialize FAILED");
			return false;
		}

Add:

	#ifdef ENABLE_ITEMSHOP
		if (!InitializePromotionCodes())
		{
			sys_err("InitializePromotionCodes FAILED");
			return false;
		}

		if (!InitializeRedeemedPromotionCodes())
		{
			sys_err("InitializeRedeemedPromotionCodes FAILED");
			return false;
		}

		if (!InitializeItemshopSpecialOffers())
		{
			sys_err("InitializeItemshopSpecialOffers FAILED");
			return false;
		}

		if (!InitializeItemshopCategoryTable())
		{
			sys_err("InitializeItemshopCategoryTable FAILED");
			return false;
		}

		if (!InitializeItemshopItemTable())
		{
			sys_err("InitializeItemshopItemTable FAILED");
			return false;
		}
	#endif

Search:

	void CClientManager::MainLoop()
	{
		SQLMsg * tmp;

		sys_log(0, "ClientManager pointer is %p", this);

		while (!m_bShutdowned)
		{

Add:

	#ifdef ENABLE_ITEMSHOP
			time_t now = time(0);
			for (auto& specialoffer_item : m_vec_itemshopSpecialOfferItems)
			{
				if (specialoffer_item.times.start_time < now && !specialoffer_item.times.is_activ)
				{
					specialoffer_item.times.is_activ = true;

					AddSingleItemshopItem(&specialoffer_item.item, specialoffer_item.llItemIndex);
				}
				else if (specialoffer_item.times.end_time < now)
				{
					RemoveSingleItemshopItem(specialoffer_item.llItemIndex);
				}
			}
	#endif

Search:

		sys_log(0, "MainLoop exited, Starting cache flushing");

		signal_timer_disable();

Add:

	#ifdef ENABLE_ITEMSHOP
		UpdateItemshopTable();
		UpdatePromotionTables();
	#endif

Search:

		DWORD dwPacketSize = 
			sizeof(DWORD) +
			sizeof(BYTE) +
			sizeof(WORD) + sizeof(WORD) + sizeof(TMobTable) * m_vec_mobTable.size() +
			sizeof(WORD) + sizeof(WORD) + sizeof(TItemTable) * m_vec_itemTable.size() +
			sizeof(WORD) + sizeof(WORD) + sizeof(TShopTable) * m_iShopTableSize +
			sizeof(WORD) + sizeof(WORD) + sizeof(TSkillTable) * m_vec_skillTable.size() +
			sizeof(WORD) + sizeof(WORD) + sizeof(TRefineTable) * m_iRefineTableSize +

Add:

	#ifdef ENABLE_ITEMSHOP
			sizeof(WORD) + sizeof(WORD) + sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize +
			sizeof(WORD) + sizeof(WORD) + sizeof(TItemshopItemTable) * m_iItemshopTableItemSize +
	#endif

Search:

		sys_log(0, "sizeof(TMobTable) = %d", sizeof(TMobTable));
		sys_log(0, "sizeof(TItemTable) = %d", sizeof(TItemTable));
		sys_log(0, "sizeof(TShopTable) = %d", sizeof(TShopTable));
		sys_log(0, "sizeof(TSkillTable) = %d", sizeof(TSkillTable));
		sys_log(0, "sizeof(TRefineTable) = %d", sizeof(TRefineTable));

Add:

	#ifdef ENABLE_ITEMSHOP
		sys_log(0, "sizeof(TItemshopCategoryTable) = %d", sizeof(TItemshopCategoryTable));
		sys_log(0, "sizeof(TItemshopItemTable) = %d", sizeof(TItemshopItemTable));
	#endif

Search:

		peer->EncodeWORD(sizeof(TSkillTable));
		peer->EncodeWORD(m_vec_skillTable.size());
		peer->Encode(&m_vec_skillTable[0], sizeof(TSkillTable) * m_vec_skillTable.size());

		peer->EncodeWORD(sizeof(TRefineTable));
		peer->EncodeWORD(m_iRefineTableSize);
		peer->Encode(m_pRefineTable, sizeof(TRefineTable) * m_iRefineTableSize);

Add:

	#ifdef ENABLE_ITEMSHOP
		peer->EncodeWORD(sizeof(TItemshopCategoryTable));
		peer->EncodeWORD(m_iItemshopTableCategorySize);
		peer->Encode(m_pItemshopTableCategories, sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize);

		peer->EncodeWORD(sizeof(TItemshopItemTable));
		peer->EncodeWORD(m_iItemshopTableItemSize);
		peer->Encode(m_pItemshopTableItems, sizeof(TItemshopItemTable) * m_iItemshopTableItemSize);
	#endif

Search:

				case HEADER_GD_RELOAD_PROTO:
					QUERY_RELOAD_PROTO();
					break;

Add:

	#ifdef ENABLE_ITEMSHOP
				case HEADER_GD_RELOAD_ITEMSHOP:
					QUERY_RELOAD_ITEMSHOP();
					break;

				case HEADER_GD_BUY_ITEMSHOP:
					ItemshopCheckBuy(peer, dwHandle, (TItemshopCheckBuy*)data);
					break;

				case HEADER_GD_PROMOTION_REDEEM:
					RedeemPromotionCode(peer, dwHandle, (TPromotionRedeem*)data);
					break;
	#endif

Search:

			if (!(thecore_heart->pulse % (thecore_heart->passes_per_sec * 5)))
			{

Add:

	#ifdef ENABLE_ITEMSHOP
				UpdateItemshopTable();
				UpdatePromotionTables();
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	void CClientManager::QUERY_RELOAD_ITEMSHOP()
	{
		UpdateItemshopTable();
		UpdatePromotionTables();

		if (!InitializePromotionCodes())
		{
			sys_err("InitializePromotionCodes FAILED");
			return;
		}

		if (!InitializeRedeemedPromotionCodes())
		{
			sys_err("InitializeRedeemedPromotionCodes FAILED");
			return;
		}

		if (!InitializeItemshopSpecialOffers())
		{
			sys_err("InitializeItemshopSpecialOffers FAILED");
			return;
		}

		if (!InitializeItemshopCategoryTable())
		{
			sys_err("InitializeItemshopCategoryTable FAILED");
			return;
		}

		if (!InitializeItemshopItemTable())
		{
			sys_err("InitializeItemshopItemTable FAILED");
			return;
		}

		for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i)
		{
			CPeer* tmp = *i;

			if (!tmp->GetChannel() || tmp == m_pkAuthPeer)
				continue;

			tmp->EncodeHeader(HEADER_DG_RELOAD_ITEMSHOP, 0,
				sizeof(WORD) + sizeof(WORD) +
				sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize +
				sizeof(TItemshopItemTable) * m_iItemshopTableItemSize
			);

			tmp->Encode(&m_iItemshopTableCategorySize, sizeof(WORD));
			tmp->Encode(&m_iItemshopTableItemSize, sizeof(WORD));
			tmp->Encode(m_pItemshopTableCategories, sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize);
			tmp->Encode(m_pItemshopTableItems, sizeof(TItemshopItemTable) * m_iItemshopTableItemSize);
		}
	}

	void CClientManager::ItemshopCheckBuy(CPeer* pkPeer, DWORD dwHandle, TItemshopCheckBuy* p)
	{
		TItemshopBuyAnswer answer;

		auto it = m_ItemshopItems.find(p->hash);

		if (it != m_ItemshopItems.end())
		{
			if (it->second.second.llLimitCount == -1)
			{
				answer.canBuy = true;
				answer.wCount = p->wCount;
				strlcpy(answer.code, p->hash, sizeof(answer.code));
				pkPeer->EncodeHeader(HEADER_DG_BUY_ITEMSHOP_ITEM, dwHandle, sizeof(TItemshopBuyAnswer));
				pkPeer->Encode(&answer, sizeof(TItemshopBuyAnswer));
				return;
			}
			else if (it->second.second.llLimitCount > 0)
			{
				answer.canBuy = true;
				strlcpy(answer.code, p->hash, sizeof(answer.code));
				it->second.second.llLimitCount--;
				// NOTE: limite item buy count 1 -> Change here if needed
				answer.wCount = 1;

				// Increase count for flush
				m_map_ItemshopLimitCountFlush[p->hash].first = it->second.first;
				m_map_ItemshopLimitCountFlush[p->hash].second++;

				pkPeer->EncodeHeader(HEADER_DG_BUY_ITEMSHOP_ITEM, dwHandle, sizeof(TItemshopBuyAnswer));
				pkPeer->Encode(&answer, sizeof(TItemshopBuyAnswer));

				if (it->second.second.llLimitCount == 0)
				{
					RemoveSingleItemshopItem(it->second.first);
				}
				else
				{
					for (auto& item : m_ItemshopItems)
					{
						if (!strcmp(item.second.second.hash, p->hash))
						{
							item.second.second.llLimitCount = it->second.second.llLimitCount;
							for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i)
							{
								CPeer* tmp = *i;

								if (!tmp->GetChannel() || tmp == m_pkAuthPeer)
									continue;

								tmp->EncodeHeader(HEADER_DG_REFRESH_ITEMSHOP_SINGLE_ITEM, 0, sizeof(TItemshopItemTable));
								tmp->Encode(&item.second.second, sizeof(TItemshopItemTable));
							}
							break;
						}
					}
				}
				return;
			}
		}

		answer.canBuy = false;
		pkPeer->EncodeHeader(HEADER_DG_BUY_ITEMSHOP_ITEM, dwHandle, sizeof(TItemshopBuyAnswer));
		pkPeer->Encode(&answer, sizeof(TItemshopBuyAnswer));
	}

	void CClientManager::RemoveSingleItemshopItem(long long llItemIndex)
	{
		for (const auto& item : m_ItemshopItems)
		{
			if (item.second.first != llItemIndex)
				continue;

			for (unsigned int i = 0; i < m_vec_itemshopSpecialOfferItems.size(); i++)
			{
				if (m_vec_itemshopSpecialOfferItems[i].llItemIndex == llItemIndex)
				{
					m_vec_itemshopSpecialOfferItems.erase(m_vec_itemshopSpecialOfferItems.begin() + i);
					break;
				}
			}

			for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i)
			{
				CPeer* tmp = *i;

				if (!tmp->GetChannel() || tmp == m_pkAuthPeer)
					continue;

				tmp->EncodeHeader(HEADER_DG_REMOVE_ITEMSHOP_SINGLE_ITEM, 0, sizeof(TItemshopItemTable));
				tmp->Encode(&item.second.second, sizeof(TItemshopItemTable));
			}

			m_ItemshopItems.erase(item.first);
			break;
		}
	}

	void CClientManager::AddSingleItemshopItem(TItemshopItemTable* item, long long llItemIndex)
	{
		m_ItemshopItems[item->hash].first = llItemIndex;
		m_ItemshopItems[item->hash].second = *item;

		for (TPeerList::iterator i = m_peerList.begin(); i != m_peerList.end(); ++i)
		{
			CPeer* tmp = *i;

			if (!tmp->GetChannel() || tmp == m_pkAuthPeer)
				continue;

			tmp->EncodeHeader(HEADER_DG_ADD_ITEMSHOP_SINGLE_ITEM, 0, sizeof(TItemshopItemTable));
			tmp->Encode(item, sizeof(TItemshopItemTable));
		}
	}

	void CClientManager::UpdateItemshopTable()
	{
		for (const auto& item : m_map_ItemshopLimitCountFlush)
		{
			const long long llIndex = item.second.first;
			const long long llLimitCount = item.second.second;
			char query[2048];

			if (llLimitCount >= 0)
			{
				snprintf(query, sizeof(query), "UPDATE itemshop.itemshop_items SET limitCount = limitCount - %lld WHERE itemIndex = %lld", llLimitCount, llIndex);
				CDBManager::instance().AsyncQuery(query, SQL_ITEMSHOP);
			}
		}

		m_map_ItemshopLimitCountFlush.clear();
	}

	void CClientManager::UpdatePromotionTables()
	{
		for (const auto& [key, val] : m_map_promotionCodeInfo)
		{
			char query[2048];

			char escape_key[PROMOTION_CODE_MAX_LEN * 2 + 1];
			CDBManager::instance().EscapeString(escape_key, key.c_str(), key.size());

			snprintf(query, sizeof(query), "UPDATE itemshop.promotion_codes SET useCount = %lld WHERE promotion_code = '%s'", val.first, escape_key);

			CDBManager::instance().AsyncQuery(query, SQL_ITEMSHOP);
		}

		for (const auto& [key, val] : m_map_redeemedPromotionFlush)
		{
			char query[2048];

			char escape_key[PROMOTION_CODE_MAX_LEN * 2 + 1];
			CDBManager::instance().EscapeString(escape_key, val.c_str(), val.size());

			snprintf(query, sizeof(query), "INSERT itemshop.promotion_redeemed (acc_id, redeemed_code, time_redeemed) VALUES (%ld, '%s', NOW())", key, escape_key);

			CDBManager::instance().AsyncQuery(query, SQL_ITEMSHOP);
		}

		m_map_redeemedPromotionFlush.clear();
	}

	void CClientManager::RedeemPromotionCode(CPeer* pkPeer, DWORD dwHandle, TPromotionRedeem* p)
	{
		TPromotionRedeemAnswer redeem_info;

		for (const auto& redeemed : m_vec_redeemedPromotionCodes[p->accID])
		{
			if (!strcmp(redeemed.c_str(), p->code))
			{
				pkPeer->EncodeHeader(HEADER_DG_PROMOTION_CODE_REDEEM, dwHandle, sizeof(TPromotionRedeemAnswer));
				redeem_info.byRedeemAnswer = REDEEM_ALREADY_REDEEMED;
				pkPeer->Encode(&redeem_info, sizeof(TPromotionRedeemAnswer));
				return;
			}
		}

		if (m_map_promotionCodeInfo.find(p->code) != m_map_promotionCodeInfo.end() && m_map_promotionCodeInfo[p->code].first > 0)
		{
			pkPeer->EncodeHeader(HEADER_DG_PROMOTION_CODE_REDEEM, dwHandle, sizeof(TPromotionRedeemAnswer) + sizeof(TPromotionItemTable) * m_map_promotionCodeInfo[p->code].second.size());
			m_map_promotionCodeInfo[p->code].first--;

			redeem_info.byRedeemAnswer = REDEEM_SUCCESS;
			redeem_info.reward_count = m_map_promotionCodeInfo[p->code].second.size();

			m_vec_redeemedPromotionCodes[p->accID].push_back(p->code);
			m_map_redeemedPromotionFlush.insert(std::pair(p->accID, p->code));

			pkPeer->Encode(&redeem_info, sizeof(TPromotionRedeemAnswer));
			pkPeer->Encode(m_map_promotionCodeInfo[p->code].second.data(), sizeof(TPromotionItemTable) * m_map_promotionCodeInfo[p->code].second.size());
		}
		else
		{
			pkPeer->EncodeHeader(HEADER_DG_PROMOTION_CODE_REDEEM, dwHandle, sizeof(TPromotionRedeemAnswer));
			redeem_info.byRedeemAnswer = REDEEM_FAIL;
			pkPeer->Encode(&redeem_info, sizeof(TPromotionRedeemAnswer));
		}
	}
	#endif

2. ClientManager.h

Add to end of class CClientManager:

	#ifdef ENABLE_ITEMSHOP
		protected:
			bool		InitializeItemshopCategoryTable();
			bool		InitializeItemshopItemTable();
			bool		InitializeItemshopSpecialOffers();
			bool		InitializePromotionCodes();
			bool		InitializeRedeemedPromotionCodes();

			void		ItemshopCheckBuy(CPeer* pkPeer, DWORD dwHandle, TItemshopCheckBuy* p);
			void		RemoveSingleItemshopItem(long long llItemIndex);
			void		AddSingleItemshopItem(TItemshopItemTable* item, long long llItemIndex);
			void		RedeemPromotionCode(CPeer* pkPeer, DWORD dwHandle, TPromotionRedeem* p);
			void		UpdatePromotionTables();
			void		UpdateItemshopTable();
		private:
			void		QUERY_RELOAD_ITEMSHOP();
			int						m_iItemshopTableCategorySize;
			TItemshopCategoryTable* m_pItemshopTableCategories;
			int						m_iItemshopTableItemSize;
			TItemshopItemTable* m_pItemshopTableItems;

			std::vector<unsigned long> m_vec_usedSeeds;

			std::unordered_map <std::string, std::pair<long long, TItemshopItemTable>> m_ItemshopItems;
			std::unordered_map <std::string, std::pair<DWORD, long long>> m_map_ItemshopLimitCountFlush;

			std::vector<TSpecialOfferItems>	m_vec_itemshopSpecialOfferItems;
			std::unordered_map<std::string, std::pair<long long, std::vector<TPromotionItemTable>>> m_map_promotionCodeInfo;
			std::unordered_map<DWORD, std::vector<std::string>> m_vec_redeemedPromotionCodes;
			std::unordered_map<DWORD, std::string> m_map_redeemedPromotionFlush;
	#endif

3. ClientManagerBoot.cpp

Add:

	#ifdef ENABLE_ITEMSHOP
	#include <boost/uuid/detail/md5.hpp>
	#include <boost/algorithm/hex.hpp>
	#include <random>
	std::string MD5_STRING(const boost::uuids::detail::md5::digest_type& digest)
	{
		const auto charDigest = reinterpret_cast<const char*>(&digest);
		std::string result;
		boost::algorithm::hex(charDigest, charDigest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(result));
		return result;
	}
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	bool CClientManager::InitializePromotionCodes()
	{
		char query[2048];

		snprintf(query, sizeof(query),
			"SELECT "
			"CODES.useCount, "
			"CODES.promotion_code, "
			"vnum, "
			"count,"
			"socket0,"
			"socket1,"
			"socket2,"

			// NOTE: Add if needed
			//"socket3,"
			//"socket4,"
			//"socket5,"

			"attrtype0,"
			"attrvalue0, "
			"attrtype1,"
			"attrvalue1, "
			"attrtype2,"
			"attrvalue2, "
			"attrtype3,"
			"attrvalue3, "
			"attrtype4,"
			"attrvalue4, "
			"attrtype5,"
			"attrvalue5, "
			"attrtype6, "
			"attrvalue6 "
			"FROM "
			"itemshop.promotion_rewards AS REWARDS "
			"LEFT JOIN itemshop.promotion_codes AS CODES ON REWARDS.code_index = CODES.index "
			"WHERE "
			"REWARDS.code_index = CODES.index AND CODES.state = 'ENABLED' "
			"AND count > 0"
		);

		std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
		SQLResult* pRes = pkMsg->Get();

		if (!m_map_promotionCodeInfo.empty())
		{
			sys_log(0, "RELOAD: itemshop offertimes");
			m_map_promotionCodeInfo.clear();
		}

		if (!pRes->uiNumRows)
		{
			return true;
		}

		MYSQL_ROW data;
		int col = 0;
		while ((data = mysql_fetch_row(pRes->pSQLResult)))
		{
			TPromotionItemTable reward;
			col = 0;

			long long llPromotionUseCount;
			char promotion_code[PROMOTION_CODE_MAX_LEN + 1];
			str_to_number(llPromotionUseCount, data[col++]);
			strlcpy(promotion_code, data[col++], sizeof(promotion_code));

			str_to_number(reward.dwVnum, data[col++]);
			str_to_number(reward.wCount, data[col++]);

			for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
			{
				str_to_number(reward.alSockets[i], data[col++]);
			}

			for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
			{
				str_to_number(reward.aAttr[i].bType, data[col++]);
				str_to_number(reward.aAttr[i].sValue, data[col++]);
			}

			m_map_promotionCodeInfo[promotion_code].first = llPromotionUseCount;
			m_map_promotionCodeInfo[promotion_code].second.push_back(reward);
		}

		return true;
	}

	bool CClientManager::InitializeRedeemedPromotionCodes()
	{
		char query[2048];

		snprintf(query, sizeof(query),
			"SELECT "
			"acc_id, "
			"redeemed_code "
			"FROM "
			"itemshop.promotion_redeemed "
		);

		std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
		SQLResult* pRes = pkMsg->Get();

		if (!m_vec_redeemedPromotionCodes.empty())
		{
			sys_log(0, "RELOAD: itemshop redeemed promotion_codes");
			m_vec_redeemedPromotionCodes.clear();
		}

		if (!pRes->uiNumRows)
		{
			return true;
		}

		MYSQL_ROW data;
		int col = 0;
		while ((data = mysql_fetch_row(pRes->pSQLResult)))
		{
			col = 0;

			DWORD accID;
			char code[PROMOTION_CODE_MAX_LEN + 1];
			str_to_number(accID, data[col++]);
			strlcpy(code, data[col++], sizeof(code));

			m_vec_redeemedPromotionCodes[accID].push_back(code);
		}

		return true;
	}

	bool CClientManager::InitializeItemshopSpecialOffers()
	{
		char query[2048];

		snprintf(query, sizeof(query),
			"SELECT "
			"itemIndex, "
			"vnum, "
			"count,"
			"ABS(price), "
			"discountPercent,"
			"limitCount, "
			"category, "
			"UNIX_TIMESTAMP(endTime),"
			"weight,"
			"socket0,"
			"socket1,"
			"socket2,"

			// NOTE: Add if needed
			//"socket3,"
			//"socket4,"
			//"socket5,"

			"attrtype0,"
			"attrvalue0, "
			"attrtype1,"
			"attrvalue1, "
			"attrtype2,"
			"attrvalue2, "
			"attrtype3,"
			"attrvalue3, "
			"attrtype4,"
			"attrvalue4, "
			"attrtype5,"
			"attrvalue5, "
			"attrtype6, "
			"attrvalue6, "
			"UNIX_TIMESTAMP(startTime), "
			"UNIX_TIMESTAMP(endTime), "
			"(UNIX_TIMESTAMP(NOW()) >= UNIX_TIMESTAMP(startTime))"
			"FROM "
			"itemshop.itemshop_items AS ITEMS "
			"LEFT JOIN itemshop.itemshop_categories AS CATEGORY ON ITEMS.category = CATEGORY.category_index "
			"WHERE "
			"ITEMS.category = CATEGORY.category_index AND CATEGORY.category_state = 'ENABLED' "
			"AND not limitCount = 0 "
			"AND count > 0 "
			"AND("
			"(UNIX_TIMESTAMP(NOW()) < UNIX_TIMESTAMP(ITEMS.endTime))"
			"AND "
			"(UNIX_TIMESTAMP(ITEMS.startTime) < UNIX_TIMESTAMP(ITEMS.endTime) ))"
			"ORDER BY UNIX_TIMESTAMP(startTime)"
		);

		std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
		SQLResult* pRes = pkMsg->Get();

		if (!m_vec_itemshopSpecialOfferItems.empty())
		{
			sys_log(0, "RELOAD: itemshop offertimes");
			m_vec_itemshopSpecialOfferItems.clear();
		}

		if (!pRes->uiNumRows)
		{
			return true;
		}

		m_vec_itemshopSpecialOfferItems.resize(pRes->uiNumRows);
		memset(&m_vec_itemshopSpecialOfferItems[0], 0, sizeof(TSpecialOfferItems) * m_vec_itemshopSpecialOfferItems.size());
		TSpecialOfferItems* specialoffer_items = &m_vec_itemshopSpecialOfferItems[0];

		MYSQL_ROW data;
		int col;
		while ((data = mysql_fetch_row(pRes->pSQLResult)))
		{
			col = 0;

			// NOTE: Random number -> First non unique after ~20.000 - 40.000 md5 hashes
			std::random_device dev;
			std::mt19937 rng(dev());
			std::uniform_int_distribution<std::mt19937::result_type> dist(1, 4294967295);

			unsigned long seed = dist(rng);

			// NOTE: To prevent duplicate hashes
			while (std::find(m_vec_usedSeeds.begin(), m_vec_usedSeeds.end(), seed) != m_vec_usedSeeds.end())
			{
				seed = dist(rng);
			}

			m_vec_usedSeeds.push_back(seed);

			boost::uuids::detail::md5 hash;
			boost::uuids::detail::md5::digest_type digest;
			hash.process_bytes((char*)&seed, sizeof(seed));
			hash.get_digest(digest);


			strlcpy(specialoffer_items->item.hash, MD5_STRING(digest).c_str(), sizeof(specialoffer_items->item.hash));

			str_to_number(specialoffer_items->llItemIndex, data[col++]);
			str_to_number(specialoffer_items->item.dwVnum, data[col++]);
			str_to_number(specialoffer_items->item.wCount, data[col++]);
			str_to_number(specialoffer_items->item.ullPrice, data[col++]);
			str_to_number(specialoffer_items->item.byDiscountPercent, data[col++]);
			str_to_number(specialoffer_items->item.llLimitCount, data[col++]);
			str_to_number(specialoffer_items->item.byCategory, data[col++]);
			str_to_number(specialoffer_items->item.end_time, data[col++]);
			str_to_number(specialoffer_items->item.weight, data[col++]);

			for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
			{
				str_to_number(specialoffer_items->item.alSockets[i], data[col++]);
			}

			for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
			{
				str_to_number(specialoffer_items->item.aAttr[i].bType, data[col++]);
				str_to_number(specialoffer_items->item.aAttr[i].sValue, data[col++]);
			}

			str_to_number(specialoffer_items->times.start_time, data[col++]);
			str_to_number(specialoffer_items->times.end_time, data[col++]);
			str_to_number(specialoffer_items->times.is_activ, data[col++]);

			++specialoffer_items;
		}

		return true;
	}

	bool CClientManager::InitializeItemshopCategoryTable()
	{
		char query[2048];

		snprintf(query, sizeof(query),
			"SELECT "
			"category_index, "
			"category_name, "
			"category_icon "
			"FROM "
			"itemshop.itemshop_categories "
			"WHERE category_state = 'ENABLED'"
		);

		std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
		SQLResult* pRes = pkMsg->Get();

		if (m_pItemshopTableCategories)
		{
			sys_log(0, "RELOAD: itemshop_categories");
			delete[] m_pItemshopTableCategories;
			m_pItemshopTableCategories = NULL;
		}

		if (!pRes->uiNumRows)
		{
			m_iItemshopTableCategorySize = 0;
			m_pItemshopTableCategories = new TItemshopCategoryTable[m_iItemshopTableCategorySize];
			memset(m_pItemshopTableCategories, 0, sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize);
			return true;
		}

		m_iItemshopTableCategorySize = pRes->uiNumRows;

		m_pItemshopTableCategories = new TItemshopCategoryTable[m_iItemshopTableCategorySize];
		memset(m_pItemshopTableCategories, 0, sizeof(TItemshopCategoryTable) * m_iItemshopTableCategorySize);

		TItemshopCategoryTable* prt = m_pItemshopTableCategories;
		MYSQL_ROW data;

		while ((data = mysql_fetch_row(pRes->pSQLResult)))
		{
			int col = 0;

			str_to_number(prt->index, data[col++]);
			strlcpy(prt->info.category, data[col++], sizeof(prt->info.category));
			strlcpy(prt->info.icon, data[col++], sizeof(prt->info.icon));

			prt++;
		}
		return true;
	}

	bool CClientManager::InitializeItemshopItemTable()
	{
		char query[2048];

		snprintf(query, sizeof(query),
			"SELECT "
			"itemIndex, "
			"vnum, "
			"count,"
			"ABS(price), "
			"discountPercent,"
			"limitCount, "
			"category, "
			"UNIX_TIMESTAMP(endTime),"
			"weight,"
			"socket0,"
			"socket1,"
			"socket2,"

			// NOTE: Add if needed
			//"socket3,"
			//"socket4,"
			//"socket5,"

			"attrtype0,"
			"attrvalue0, "
			"attrtype1,"
			"attrvalue1, "
			"attrtype2,"
			"attrvalue2, "
			"attrtype3,"
			"attrvalue3, "
			"attrtype4,"
			"attrvalue4, "
			"attrtype5,"
			"attrvalue5, "
			"attrtype6, "
			"attrvalue6 "
			"FROM "
			"itemshop.itemshop_items AS ITEMS "
			"LEFT JOIN itemshop.itemshop_categories AS CATEGORY ON ITEMS.category = CATEGORY.category_index "
			"WHERE "
			"ITEMS.category = CATEGORY.category_index AND CATEGORY.category_state = 'ENABLED' "
			"AND not limitCount = 0 "
			"AND count > 0 "
			"AND("
			"(UNIX_TIMESTAMP(ITEMS.startTime) <= 0 AND UNIX_TIMESTAMP(ITEMS.endTime) <= 0) OR"
			"(UNIX_TIMESTAMP(NOW()) >= UNIX_TIMESTAMP(ITEMS.startTime) AND UNIX_TIMESTAMP(NOW()) < UNIX_TIMESTAMP(ITEMS.endTime))"
			")"
		);

		std::unique_ptr<SQLMsg> pkMsg(CDBManager::instance().DirectQuery(query));
		SQLResult* pRes = pkMsg->Get();

		m_ItemshopItems.clear();

		if (m_pItemshopTableItems)
		{
			sys_log(0, "RELOAD: itemshop_items");
			delete[] m_pItemshopTableItems;
			m_pItemshopTableItems = NULL;
		}

		if (!pRes->uiNumRows)
		{
			m_iItemshopTableItemSize = 0;
			m_pItemshopTableItems = new TItemshopItemTable[m_iItemshopTableItemSize];
			memset(m_pItemshopTableItems, 0, sizeof(TItemshopItemTable) * m_iItemshopTableItemSize);
			return true;
		}

		m_iItemshopTableItemSize = pRes->uiNumRows;

		m_pItemshopTableItems = new TItemshopItemTable[m_iItemshopTableItemSize];
		memset(m_pItemshopTableItems, 0, sizeof(TItemshopItemTable) * m_iItemshopTableItemSize);

		TItemshopItemTable* prt = m_pItemshopTableItems;
		MYSQL_ROW data;

		while ((data = mysql_fetch_row(pRes->pSQLResult)))
		{
			int col = 0;

			// NOTE: Random number -> First non unique after ~20.000 - 40.000 md5 hashes
			std::random_device dev;
			std::mt19937 rng(dev());
			std::uniform_int_distribution<std::mt19937::result_type> dist(1, 4294967295);

			unsigned long seed = dist(rng);

			// NOTE: To prevent duplicate hashes
			while (std::find(m_vec_usedSeeds.begin(), m_vec_usedSeeds.end(), seed) != m_vec_usedSeeds.end())
			{
				seed = dist(rng);
			}

			m_vec_usedSeeds.push_back(seed);

			boost::uuids::detail::md5 hash;
			boost::uuids::detail::md5::digest_type digest;
			hash.process_bytes((char*)&seed, sizeof(seed));
			hash.get_digest(digest);
			strlcpy(prt->hash, MD5_STRING(digest).c_str(), sizeof(prt->hash));

			long long llIndex;
			str_to_number(llIndex, data[col++]);
			str_to_number(prt->dwVnum, data[col++]);
			str_to_number(prt->wCount, data[col++]);
			str_to_number(prt->ullPrice, data[col++]);
			str_to_number(prt->byDiscountPercent, data[col++]);
			str_to_number(prt->llLimitCount, data[col++]);
			str_to_number(prt->byCategory, data[col++]);
			str_to_number(prt->end_time, data[col++]);
			str_to_number(prt->weight, data[col++]);

			for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
			{
				str_to_number(prt->alSockets[i], data[col++]);
			}

			for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; i++)
			{
				str_to_number(prt->aAttr[i].bType, data[col++]);
				str_to_number(prt->aAttr[i].sValue, data[col++]);
			}

			m_ItemshopItems[prt->hash].first = llIndex;
			m_ItemshopItems[prt->hash].second = *prt;

			prt++;
		}

		m_vec_usedSeeds.clear();

		return true;
	}
	#endif

4. DBManager.h

Search:

		SQL_PLAYER,
		SQL_ACCOUNT,
		SQL_COMMON,

Add:

	#ifdef ENABLE_ITEMSHOP
		SQL_ITEMSHOP,
	#endif

5. Main.cpp

Search:

		if (CConfig::instance().GetValue("SQL_COMMON", line, 256))
		{
			sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
			sys_log(0, "connecting to MySQL server (common)");

			int iRetry = 5;

			do
			{
				if (CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword))
				{
					sys_log(0, "   OK");
					break;
				}

				sys_log(0, "   failed, retrying in 5 seconds");
				fprintf(stderr, "   failed, retrying in 5 seconds");
				sleep(5);
			} while (iRetry--);
			fprintf(stderr, "Success COMMON\n");
		}
		else
		{
			sys_err("SQL_COMMON not configured");
			return false;
		}

Add:

	#ifdef ENABLE_ITEMSHOP
		if (CConfig::instance().GetValue("SQL_ITEMSHOP", line, 256))
		{
			sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
			sys_log(0, "connecting to MySQL server (itemshop)");

			int iRetry = 5;

			do
			{
				if (CDBManager::instance().Connect(SQL_ITEMSHOP, szAddr, iPort, szDB, szUser, szPassword))
				{
					sys_log(0, "   OK");
					break;
				}

				sys_log(0, "   failed, retrying in 5 seconds");
				fprintf(stderr, "   failed, retrying in 5 seconds");
				sleep(5);
			} while (iRetry--);
			fprintf(stderr, "Success ITEMSHOP\n");
		}
		else
		{
			sys_err("SQL_ITEMSHOP not configured");
			return false;
		}
	#endif