UserInterface

1. defines.h

Add:

	#define ENABLE_ITEMSHOP

2. Packet.h

Search:

		HEADER_CG_TIME_SYNC							= 0xfc,
		HEADER_CG_PONG								= 0xfe,

Add above:

	#ifdef ENABLE_ITEMSHOP
		HEADER_CG_BUY_ITEMSHOP_ITEM						= 240,
		HEADER_CG_PROMOTION							= 241,
	#endif

Search:

		HEADER_GC_HANDSHAKE_OK							= 0xfc,
		HEADER_GC_PHASE								= 0xfd,

Add above:

	#ifdef ENABLE_ITEMSHOP
		HEADER_GC_ITEMSHOP							= 240,
	#endif

Add to end of file above #pragma pack(pop):

	#ifdef ENABLE_ITEMSHOP
	enum
	{
		ITEMSHOP_CATEGORY_MAX_LEN = 24,
		ITEMSHOP_ICON_MAX_LEN = 24,
		ITEMSHOP_HASH_MAX_LEN = 32,
		PROMOTION_CODE_MAX_LEN = 48,
	};

	typedef struct SItemshopCategoryInfo
	{
		char					category[ITEMSHOP_CATEGORY_MAX_LEN + 1];
		char					icon[ITEMSHOP_ICON_MAX_LEN + 1];
	}TItemshopCategoryInfo;

	typedef struct SPacketGCitemshopCategorySize
	{
		DWORD size;
	} TPacketGCitemshopCategorySize;

	typedef struct SPacketGCItemshopItemSize
	{
		DWORD size;
	} TPacketGCItemshopItemSize;

	typedef struct SPacketGCItemshopInfo
	{
		BYTE header;
		DWORD subheader;
	} TPacketGCItemshopInfo;

	enum
	{
		SUBHEADER_ITEMSHOP_REFRESH_ITEMS,
		SUBHEADER_ITEMSHOP_REFRESH_COINS,
		SUBHEADER_ITEMSHOP_REFRESH_SINGLE_ITEM,
		SUBHEADER_ITEMSHOP_REMOVE_SINGLE_ITEM,
		SUBHEADER_ITEMSHOP_ADD_SINGLE_ITEM,
		SUBHEADER_PROMOTION_CODE_REWARDS,
	};

	typedef struct SItemshopItemTable
	{
		char					hash[ITEMSHOP_HASH_MAX_LEN + 1];
		unsigned long long		ullPrice;
		BYTE					byDiscountPercent;
		long long				llLimitCount;
		BYTE					byCategory;
		DWORD					dwVnum;
		WORD					wCount;
		time_t					end_time;
		long					alSockets[ITEM_SOCKET_SLOT_MAX_NUM];
		TPlayerItemAttribute	aAttr[ITEM_ATTRIBUTE_SLOT_MAX_NUM];
		BYTE					weight;
	} TItemshopItemTable;

	typedef struct SPromotionItemTable
	{
		DWORD					dwVnum;
		WORD					wCount;
		long					alSockets[ITEM_SOCKET_SLOT_MAX_NUM];
		TPlayerItemAttribute	aAttr[ITEM_ATTRIBUTE_SLOT_MAX_NUM];
	} TPromotionItemTable;

	typedef struct SPacketCGBuyItemshopItem
	{
		BYTE	header;
		char	hash[ITEMSHOP_HASH_MAX_LEN + 1];
		WORD	wCount;
	} TPacketCGBuyItemshopItem;

	typedef struct SPacketCGRedeemPromotionCode
	{
		BYTE	header;
		char	promotion_code[PROMOTION_CODE_MAX_LEN + 1];
	} TPacketCGRedeemPromotionCode;
	#endif

3. PythonApplication.cpp

Search:

	time_t CPythonApplication::GetServerTimeStamp()
	{
		return (time(0) - m_tLocalStartTime) + m_tServerTime;
	}

Add:

	#ifdef ENABLE_ITEMSHOP
	time_t CPythonApplication::GetUNIX()
	{
		return time(0);
	}
	#endif

4. PythonApplication.h

Search:

			time_t GetServerTimeStamp();

Add:

	#ifdef ENABLE_ITEMSHOP
			time_t GetUNIX();
	#endif

5. PythonApplicationModule.cpp

Search:

	PyObject * appGetGlobalTimeStamp(PyObject * poSelf, PyObject * poArgs)
	{
		return Py_BuildValue("i", CPythonApplication::Instance().GetServerTimeStamp());
	}

Add:

	#ifdef ENABLE_ITEMSHOP
	PyObject* appGetUNIX(PyObject* poSelf, PyObject* poArgs)
	{
		return Py_BuildValue("i", CPythonApplication::Instance().GetUNIX());
	}
	#endif

Search:

			{ "GetGlobalTimeStamp",			appGetGlobalTimeStamp,			METH_VARARGS },

Add:

	#ifdef ENABLE_ITEMSHOP
			{ "GetUNIX",					appGetUNIX,						METH_VARARGS },
	#endif

Add to end of file above }:

	#ifdef ENABLE_ITEMSHOP
		PyModule_AddIntConstant(poModule, "ENABLE_ITEMSHOP", 1);
	#else
		PyModule_AddIntConstant(poModule, "ENABLE_ITEMSHOP", 0);
	#endif

6. PythonItemModule.cpp

Search:

		PyModule_AddIntConstant(poModule, "ITEM_FLAG_CONFIRM_WHEN_USE",	CItemData::ITEM_FLAG_CONFIRM_WHEN_USE);

Add:

	#ifdef ENABLE_ITEMSHOP
		PyModule_AddIntConstant(poModule, "ITEM_FLAG_STACKABLE",		CItemData::ITEM_FLAG_STACKABLE);
	#endif

7. PythonNetworkStream.cpp

Search:

		public:
			CMainPacketHeaderMap()
			{

Add:

	#ifdef ENABLE_ITEMSHOP
				Set(HEADER_GC_ITEMSHOP, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemshopInfo), STATIC_SIZE_PACKET));
	#endif

8. PythonNetworkStream.h

Search:

			bool RecvMallOpenPacket();
			bool RecvMallItemSetPacket();
			bool RecvMallItemDelPacket();

Add above:

	#ifdef ENABLE_ITEMSHOP
			bool RecvItemshopPacket();
			bool SendBuyItemshopItemPacket(const char* hash, WORD wCount);
			bool SendRedeemPromotionCodePacket(const char* promotion_code);
	#endif

9. PythonNetworkStreamModule.cpp

Search:

	void initnet()

Add above:

	#ifdef ENABLE_ITEMSHOP
	PyObject* netSendBuyItemshopItemPacket(PyObject* poSelf, PyObject* poArgs)
	{
		char* szHash;
		if (!PyTuple_GetString(poArgs, 0, &szHash))
			return Py_BuildException();

		int count;
		if (!PyTuple_GetInteger(poArgs, 1, &count))
			return Py_BuildException();

		CPythonNetworkStream::Instance().SendBuyItemshopItemPacket(szHash, count);

		return Py_BuildNone();
	}

	PyObject* netSendRedeemPromotionCodePacket(PyObject* poSelf, PyObject* poArgs)
	{
		char* szCode;
		if (!PyTuple_GetString(poArgs, 0, &szCode))
			return Py_BuildException();

		if (!strcmp(szCode, ""))
			return Py_BuildNone();

		if (strlen(szCode) <= 0)
			return Py_BuildNone();

		CPythonNetworkStream::Instance().SendRedeemPromotionCodePacket(szCode);

		return Py_BuildNone();
	}
	#endif

Search:

	void initnet()
	{
		static PyMethodDef s_methods[] =
		{

Add:

	#ifdef ENABLE_ITEMSHOP
			{ "SendBuyItemshopItemPacket",			netSendBuyItemshopItemPacket,			METH_VARARGS },
			{ "SendRedeemPromotionCodePacket",		netSendRedeemPromotionCodePacket,		METH_VARARGS },
	#endif

10. PythonNetworkStreamPhaseGame.cpp

Search:

				default:
					ret = RecvDefaultPacket(header);
					break;

Add above:

	#ifdef ENABLE_ITEMSHOP
				case HEADER_GC_ITEMSHOP:
					ret = RecvItemshopPacket();
					break;
	#endif

Add to end of file:

	#ifdef ENABLE_ITEMSHOP
	bool CPythonNetworkStream::SendBuyItemshopItemPacket(const char* hash, WORD wCount)
	{
		TPacketCGBuyItemshopItem p;
		p.header = HEADER_CG_BUY_ITEMSHOP_ITEM;
		p.wCount = wCount;
		strncpy(p.hash, hash, sizeof(p.hash));
	
		if (!Send(sizeof(p), &p))
		{
			return false;
		}
		return true;
	}
	
	bool CPythonNetworkStream::SendRedeemPromotionCodePacket(const char* promotion_code)
	{
		TPacketCGRedeemPromotionCode p;
		p.header = HEADER_CG_PROMOTION;
		strncpy(p.promotion_code, promotion_code, sizeof(p.promotion_code) - 1);
		if (!Send(sizeof(p), &p))
		{
			return false;
		}
		return true;
	}
	
	bool CPythonNetworkStream::RecvItemshopPacket()
	{
		TPacketGCItemshopInfo packet;
		if (!Recv(sizeof(packet), &packet))
			return false;
	
		DWORD subHeader = packet.subheader;
	
		switch (subHeader)
		{
		case SUBHEADER_ITEMSHOP_REFRESH_ITEMS:
		{
			TPacketGCitemshopCategorySize itemshop_category_size;
			if (!Recv(sizeof(itemshop_category_size), &itemshop_category_size))
				return false;
	
			std::map<BYTE, TItemshopCategoryInfo> itemshop_categories;
	
			BYTE key = 0;
			TItemshopCategoryInfo category_info;
	
			for (int i = 0; i < itemshop_category_size.size; i++)
			{
				if (!Recv(sizeof(BYTE), &key))
					return false;
	
				if (!Recv(sizeof(category_info), &category_info))
					return false;
	
				itemshop_categories.insert(std::pair(key, category_info));
			}
	
			TPacketGCItemshopItemSize itemshop_item_size;
			if (!Recv(sizeof(itemshop_item_size), &itemshop_item_size))
				return false;
	
			std::map<BYTE, std::vector<TItemshopItemTable>> itemshop_items;
			size_t vec_size = 0;
			TItemshopItemTable table{};
			for (int i = 0; i < itemshop_item_size.size; i++) {
				if (!Recv(sizeof(BYTE), &key)) {
					return false;
				}
				if (!Recv(sizeof(size_t), &vec_size)) {
					return false;
				}

				std::vector<TItemshopItemTable> itemVec;
				itemVec.resize(vec_size);

				if (!Recv(sizeof(TItemshopItemTable) * vec_size, itemVec.data()))
				{
					return false;
				}

				itemshop_items[key] = itemVec;
			}
	
			// NOTE: Sort items in categories
			for (auto& vec_item : itemshop_items)
			{
				std::sort(vec_item.second.begin(), vec_item.second.end(), [](TItemshopItemTable const& f, TItemshopItemTable const& s) {
					return f.end_time > s.end_time || f.end_time == s.end_time &&
						(f.llLimitCount > s.llLimitCount || f.llLimitCount == s.llLimitCount &&
							(f.byDiscountPercent > s.byDiscountPercent || f.byDiscountPercent == s.byDiscountPercent &&
								(f.weight > s.weight || f.weight == s.weight &&
									(f.dwVnum > s.dwVnum))));
					}
				);
			}
	
			PyObject* py_itemshop_infos = PyList_New(0);
	
			for (const auto& [key, cat] : itemshop_items)
			{
				PyObject* py_itemshop_category = PyDict_New();
	
				PyDict_SetItem(py_itemshop_category, PyString_FromString("index"), PyInt_FromLong(key));
				PyDict_SetItem(py_itemshop_category, PyString_FromString("category"), PyString_FromString(itemshop_categories.at(key).category));
				PyDict_SetItem(py_itemshop_category, PyString_FromString("icon"), PyString_FromString(itemshop_categories.at(key).icon));
	
				PyObject* py_itemshop_items = PyList_New(0);
	
				for (const auto& item : cat)
				{
					PyObject* py_itemshop_item = PyDict_New();
					PyDict_SetItem(py_itemshop_item, PyString_FromString("vnum"), PyInt_FromLong(item.dwVnum));
	
					PyObject* sockets = PyList_New(0);
					for (int socket = 0; socket < ITEM_SOCKET_SLOT_MAX_NUM; socket++)
					{
						PyList_Append(sockets, PyInt_FromLong(item.alSockets[socket]));
					}
					PyDict_SetItem(py_itemshop_item, PyString_FromString("sockets"), sockets);
	
					PyObject* attrList = PyList_New(0);
	
					for (const auto& attr : item.aAttr)
					{
						PyList_Append(attrList, Py_BuildValue("ii", attr.bType, attr.sValue));
					}
					PyDict_SetItem(py_itemshop_item, PyString_FromString("attr"), attrList);
					PyDict_SetItem(py_itemshop_item, PyString_FromString("price"), PyInt_FromLong(item.ullPrice));
					PyDict_SetItem(py_itemshop_item, PyString_FromString("count"), PyInt_FromLong(item.wCount));
					PyDict_SetItem(py_itemshop_item, PyString_FromString("hash"), PyString_FromString(item.hash));
					PyDict_SetItem(py_itemshop_item, PyString_FromString("discount"), PyInt_FromLong(item.byDiscountPercent));
					PyDict_SetItem(py_itemshop_item, PyString_FromString("limitCount"), PyInt_FromLong(item.llLimitCount));
					PyDict_SetItem(py_itemshop_item, PyString_FromString("endTime"), PyInt_FromLong(item.end_time));
	
					PyList_Append(py_itemshop_items, py_itemshop_item);
				}
				PyDict_SetItem(py_itemshop_category, PyString_FromString("items"), py_itemshop_items);
				PyList_Append(py_itemshop_infos, py_itemshop_category);
			}
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_UPDATE_ITEMSHOP_ITEMS", Py_BuildValue("(O)", py_itemshop_infos));
		}
		break;
		case SUBHEADER_ITEMSHOP_REFRESH_SINGLE_ITEM:
		{
			TItemshopItemTable item;
			if (!Recv(sizeof(item), &item))
				return false;
	
			PyObject* py_itemshop_item = PyDict_New();
			PyDict_SetItem(py_itemshop_item, PyString_FromString("vnum"), PyInt_FromLong(item.dwVnum));
			PyObject* sockets = PyList_New(0);
			for (int socket = 0; socket < ITEM_SOCKET_SLOT_MAX_NUM; socket++)
			{
				PyList_Append(sockets, PyInt_FromLong(item.alSockets[socket]));
			}
			PyDict_SetItem(py_itemshop_item, PyString_FromString("sockets"), sockets);
	
			PyObject* attrList = PyList_New(0);
	
			for (int attr = 0; attr < ITEM_ATTRIBUTE_SLOT_MAX_NUM; attr++)
			{
				PyList_Append(attrList, Py_BuildValue("ii", item.aAttr[attr].bType, item.aAttr[attr].sValue));
			}
			PyDict_SetItem(py_itemshop_item, PyString_FromString("attr"), attrList);
			PyDict_SetItem(py_itemshop_item, PyString_FromString("price"), PyInt_FromLong(item.ullPrice));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("count"), PyInt_FromLong(item.wCount));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("hash"), PyString_FromString(item.hash));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("discount"), PyInt_FromLong(item.byDiscountPercent));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("limitCount"), PyInt_FromLong(item.llLimitCount));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("endTime"), PyInt_FromLong(item.end_time));
	
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_UPDATE_ITEMSHOP_ITEM", Py_BuildValue("(O)", py_itemshop_item));
		}
		break;
		case SUBHEADER_ITEMSHOP_REFRESH_COINS:
		{
			unsigned long long ullCoins;
	
			if (!Recv(sizeof(unsigned long long), &ullCoins))
				return false;
	
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_UPDATE_ITEMSHOP_COINS", Py_BuildValue("(L)", ullCoins));
		}
		break;
		case SUBHEADER_ITEMSHOP_REMOVE_SINGLE_ITEM:
		{
			TItemshopItemTable item;
			if (!Recv(sizeof(item), &item))
				return false;
	
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_REMOVE_ITEMSHOP_ITEM", Py_BuildValue("(s)", item.hash));
		}
		break;
	
		case SUBHEADER_ITEMSHOP_ADD_SINGLE_ITEM:
		{
			TItemshopCategoryInfo category_info;
			if (!Recv(sizeof(category_info), &category_info))
				return false;
	
			TItemshopItemTable item;
			if (!Recv(sizeof(item), &item))
				return false;
	
			PyObject* py_itemshop_category = PyDict_New();
	
			PyDict_SetItem(py_itemshop_category, PyString_FromString("index"), PyInt_FromLong(item.byCategory));
			PyDict_SetItem(py_itemshop_category, PyString_FromString("category"), PyString_FromString(category_info.category));
			PyDict_SetItem(py_itemshop_category, PyString_FromString("icon"), PyString_FromString(category_info.icon));
	
			PyObject* py_itemshop_items = PyList_New(0);
			PyObject* py_itemshop_item = PyDict_New();
			PyDict_SetItem(py_itemshop_item, PyString_FromString("vnum"), PyInt_FromLong(item.dwVnum));
	
			PyObject* sockets = PyList_New(0);
			for (int socket = 0; socket < ITEM_SOCKET_SLOT_MAX_NUM; socket++)
			{
				PyList_Append(sockets, PyInt_FromLong(item.alSockets[socket]));
			}
			PyDict_SetItem(py_itemshop_item, PyString_FromString("sockets"), sockets);
	
			PyObject* attrList = PyList_New(0);
	
			for (const auto& attr : item.aAttr)
			{
				PyList_Append(attrList, Py_BuildValue("ii", attr.bType, attr.sValue));
			}
			PyDict_SetItem(py_itemshop_item, PyString_FromString("attr"), attrList);
			PyDict_SetItem(py_itemshop_item, PyString_FromString("price"), PyInt_FromLong(item.ullPrice));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("count"), PyInt_FromLong(item.wCount));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("hash"), PyString_FromString(item.hash));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("discount"), PyInt_FromLong(item.byDiscountPercent));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("limitCount"), PyInt_FromLong(item.llLimitCount));
			PyDict_SetItem(py_itemshop_item, PyString_FromString("endTime"), PyInt_FromLong(item.end_time));
	
			PyList_Append(py_itemshop_items, py_itemshop_item);
			PyDict_SetItem(py_itemshop_category, PyString_FromString("items"), py_itemshop_items);
	
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_ADD_ITEMSHOP_ITEM", Py_BuildValue("(O)", py_itemshop_category));
		}
		break;
	
		case SUBHEADER_PROMOTION_CODE_REWARDS:
		{
			BYTE answer;
			if (!Recv(sizeof(BYTE), &answer))
				return false;
	
			size_t size;
			if (!Recv(sizeof(size_t), &size))
				return false;
			std::vector <TPromotionItemTable> items;
	
			items.clear();
			items.resize(size);
	
			if (size && !Recv(sizeof(TPromotionItemTable) * size, &items[0]))
				return false;
	
			PyObject* py_itemshop_items = PyList_New(0);
	
			for (const auto& item : items)
			{
				PyObject* py_itemshop_item = PyDict_New();
				PyDict_SetItem(py_itemshop_item, PyString_FromString("vnum"), PyInt_FromLong(item.dwVnum));
				PyObject* sockets = PyList_New(0);
				for (int socket = 0; socket < ITEM_SOCKET_SLOT_MAX_NUM; socket++)
				{
					PyList_Append(sockets, PyInt_FromLong(item.alSockets[socket]));
				}
				PyDict_SetItem(py_itemshop_item, PyString_FromString("sockets"), sockets);
	
				PyObject* attrList = PyList_New(0);
	
				for (int attr = 0; attr < ITEM_ATTRIBUTE_SLOT_MAX_NUM; attr++)
				{
					PyList_Append(attrList, Py_BuildValue("ii", item.aAttr[attr].bType, item.aAttr[attr].sValue));
				}
				PyDict_SetItem(py_itemshop_item, PyString_FromString("attr"), attrList);
				PyDict_SetItem(py_itemshop_item, PyString_FromString("count"), PyInt_FromLong(item.wCount));
	
				PyList_Append(py_itemshop_items, py_itemshop_item);
			}
			PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_PROMOTION_CODE_REWARDS", Py_BuildValue("(iO)", answer, py_itemshop_items));
		}
		break;
		}
		return true;
	}
	#endif