ML-Client: Item Names client sided

This commit is contained in:
rtw1x1
2026-01-21 01:51:43 +00:00
parent e40fdcb7d6
commit 9eea8d9bc0
5 changed files with 161 additions and 0 deletions

View File

@@ -298,6 +298,8 @@ enum
HEADER_GC_DRAGON_SOUL_REFINE = 209,
HEADER_GC_RESPOND_CHANNELSTATUS = 210,
HEADER_GC_ITEM_GET = 211,
HEADER_GC_KEY_AGREEMENT_COMPLETED = 0xfa, // _IMPROVED_PACKET_ENCRYPTION_
HEADER_GC_KEY_AGREEMENT = 0xfb, // _IMPROVED_PACKET_ENCRYPTION_
HEADER_GC_HANDSHAKE_OK = 0xfc, // 252
@@ -1630,6 +1632,15 @@ typedef struct packet_set_item
TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_SLOT_MAX_NUM];
} TPacketGCItemSet;
typedef struct packet_item_get
{
uint8_t header;
uint32_t dwItemVnum;
uint8_t bCount;
uint8_t bArg; // 0: normal, 1: from party member
char szFromName[CHARACTER_NAME_MAX_LEN + 1];
} TPacketGCItemGet;
typedef struct packet_use_item
{
uint8_t header;

View File

@@ -71,6 +71,7 @@ class CMainPacketHeaderMap : public CNetworkPacketHeaderMap
Set(HEADER_GC_ITEM_DEL, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemDel), STATIC_SIZE_PACKET));
Set(HEADER_GC_ITEM_SET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet), STATIC_SIZE_PACKET));
Set(HEADER_GC_ITEM_GET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemGet), STATIC_SIZE_PACKET));
Set(HEADER_GC_ITEM_USE, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemUse), STATIC_SIZE_PACKET));
Set(HEADER_GC_ITEM_UPDATE, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemUpdate), STATIC_SIZE_PACKET));

View File

@@ -447,6 +447,7 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton<CPythonNet
bool RecvItemDelPacket(); // Alarm to python
bool RecvItemSetPacket(); // Alarm to python
bool RecvItemGetPacket(); // Alarm to python
bool RecvItemUsePacket(); // Alarm to python
bool RecvItemUpdatePacket(); // Alarm to python
bool RecvItemGroundAddPacket();
@@ -604,6 +605,7 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton<CPythonNet
void __RecvCharacterUpdatePacket(SNetworkUpdateActorData * pkNetUpdateActorData);
void __FilterInsult(char* szLine, UINT uLineLen);
void __LocalizeItemLinks(char* buf, size_t bufSize);
void __SetGuildID(DWORD id);

View File

@@ -320,6 +320,10 @@ void CPythonNetworkStream::GamePhase()
ret = RecvItemSetPacket();
break;
case HEADER_GC_ITEM_GET:
ret = RecvItemGetPacket();
break;
case HEADER_GC_ITEM_USE:
ret = RecvItemUsePacket();
break;
@@ -1293,6 +1297,111 @@ void CPythonNetworkStream::__ConvertEmpireText(DWORD dwEmpireID, char* szText)
}
}
void CPythonNetworkStream::__LocalizeItemLinks(char* buf, size_t bufSize)
{
// Replace item names in hyperlinks with client-side localized names
// Format: |Hitem:VNUM:flags:socket0:socket1:socket2...|h[ItemName]|h
// Early exit: skip processing if no item links exist
if (!strstr(buf, "|Hitem:"))
return;
char* pSearch = buf;
char result[2048];
char* pResult = result;
char* pResultEnd = result + sizeof(result) - 1;
while (*pSearch && pResult < pResultEnd)
{
// Look for |Hitem:
if (strncmp(pSearch, "|Hitem:", 7) == 0)
{
char* pLinkStart = pSearch;
pSearch += 7;
// Extract vnum (first hex value after "item:")
DWORD dwVnum = 0;
while (*pSearch && *pSearch != ':' && *pSearch != '|')
{
if (*pSearch >= '0' && *pSearch <= '9')
dwVnum = dwVnum * 16 + (*pSearch - '0');
else if (*pSearch >= 'a' && *pSearch <= 'f')
dwVnum = dwVnum * 16 + (*pSearch - 'a' + 10);
else if (*pSearch >= 'A' && *pSearch <= 'F')
dwVnum = dwVnum * 16 + (*pSearch - 'A' + 10);
pSearch++;
}
// Find |h[ which marks the start of the item name
char* pNameStart = strstr(pSearch, "|h[");
if (pNameStart)
{
pNameStart += 3; // Skip "|h["
// Find ]|h which marks the end of the item name
char* pNameEnd = strstr(pNameStart, "]|h");
if (pNameEnd)
{
// Get the client-side item name
CItemData* pItemData;
const char* szLocalName = NULL;
if (CItemManager::Instance().GetItemDataPointer(dwVnum, &pItemData))
szLocalName = pItemData->GetName();
// Copy everything from link start to |h[
size_t copyLen = pNameStart - pLinkStart;
if (pResult + copyLen < pResultEnd)
{
memcpy(pResult, pLinkStart, copyLen);
pResult += copyLen;
}
// Insert the localized name (or original if not found)
const char* szName = szLocalName ? szLocalName : pNameStart;
size_t nameLen = szLocalName ? strlen(szLocalName) : (pNameEnd - pNameStart);
if (pResult + nameLen < pResultEnd)
{
if (szLocalName)
{
memcpy(pResult, szLocalName, nameLen);
}
else
{
memcpy(pResult, pNameStart, nameLen);
}
pResult += nameLen;
}
// Copy ]|h
if (pResult + 3 < pResultEnd)
{
memcpy(pResult, "]|h", 3);
pResult += 3;
}
pSearch = pNameEnd + 3; // Skip past ]|h
continue;
}
}
// If we couldn't parse properly, just copy the character and continue
if (pResult < pResultEnd)
*pResult++ = *pLinkStart;
pSearch = pLinkStart + 1;
}
else
{
if (pResult < pResultEnd)
*pResult++ = *pSearch;
pSearch++;
}
}
*pResult = '\0';
strncpy(buf, result, bufSize - 1);
buf[bufSize - 1] = '\0';
}
bool CPythonNetworkStream::RecvChatPacket()
{
TPacketGCChat kChat;
@@ -1309,6 +1418,9 @@ bool CPythonNetworkStream::RecvChatPacket()
buf[uChatSize]='\0';
// Localize item names in hyperlinks for multi-language support
__LocalizeItemLinks(buf, sizeof(buf));
if (kChat.type >= CHAT_TYPE_MAX_NUM)
return true;

View File

@@ -7,6 +7,7 @@
#include "PythonCharacterManager.h"
#include "AbstractPlayer.h"
#include "GameLib/ItemManager.h"
//////////////////////////////////////////////////////////////////////////
// SafeBox
@@ -254,6 +255,40 @@ bool CPythonNetworkStream::RecvItemSetPacket()
return true;
}
bool CPythonNetworkStream::RecvItemGetPacket()
{
TPacketGCItemGet packet;
if (!Recv(sizeof(TPacketGCItemGet), &packet))
return false;
CItemData* pItemData;
if (!CItemManager::Instance().GetItemDataPointer(packet.dwItemVnum, &pItemData))
{
TraceError("CPythonNetworkStream::RecvItemGetPacket - Unknown item vnum %u", packet.dwItemVnum);
return true;
}
if (packet.bArg == 0)
{
// Normal pickup
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_ItemGet",
Py_BuildValue("(si)", pItemData->GetName(), packet.bCount));
}
else if (packet.bArg == 1)
{
// Received from party member
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_ItemGetFromParty",
Py_BuildValue("(ssi)", pItemData->GetName(), packet.szFromName, packet.bCount));
}
else if (packet.bArg == 2)
{
// Delivered to party member
PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_ItemDeliverToParty",
Py_BuildValue("(ssi)", pItemData->GetName(), packet.szFromName, packet.bCount));
}
return true;
}
bool CPythonNetworkStream::RecvItemUsePacket()
{