From 1903420820bce5606e25ee28b9a03f45f039f4a7 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Wed, 21 Jan 2026 00:22:18 +0000 Subject: [PATCH 1/3] ML-Client: NPC Names client sided --- src/UserInterface/Packet.h | 1 + src/UserInterface/PythonNetworkStreamPhaseGame.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/UserInterface/Packet.h b/src/UserInterface/Packet.h index 7a5f4e8..51864d4 100644 --- a/src/UserInterface/Packet.h +++ b/src/UserInterface/Packet.h @@ -2281,6 +2281,7 @@ typedef struct SPacketGCNPCPosition struct TNPCPosition { uint8_t bType; + uint32_t dwVnum; char name[CHARACTER_NAME_MAX_LEN+1]; int32_t x; int32_t y; diff --git a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp index deb133b..335201e 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGame.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGame.cpp @@ -4173,7 +4173,15 @@ bool CPythonNetworkStream::RecvNPCList() if (!Recv(sizeof(TNPCPosition), &NPCPosition)) return false; - CPythonMiniMap::Instance().RegisterAtlasMark(NPCPosition.bType, NPCPosition.name, NPCPosition.x, NPCPosition.y); + const char* c_szName = nullptr; + if (CPythonNonPlayer::Instance().GetName(NPCPosition.dwVnum, &c_szName)) + { + CPythonMiniMap::Instance().RegisterAtlasMark(NPCPosition.bType, c_szName, NPCPosition.x, NPCPosition.y); + } + else + { + CPythonMiniMap::Instance().RegisterAtlasMark(NPCPosition.bType, NPCPosition.name, NPCPosition.x, NPCPosition.y); + } } return true; From e40fdcb7d60c1b1f102442774ae0aa1ba11090b9 Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Wed, 21 Jan 2026 01:07:17 +0000 Subject: [PATCH 2/3] ML-Client: NPC Names client sided #2 --- .../PythonNetworkStreamPhaseGameActor.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp b/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp index 775605c..296b65e 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGameActor.cpp @@ -158,7 +158,18 @@ bool CPythonNetworkStream::RecvCharacterAdditionalInfo() if(kNetActorData.m_dwVID == chrInfoPacket.dwVID) { - kNetActorData.m_stName = chrInfoPacket.name; + if (kNetActorData.m_bType == CActorInstance::TYPE_NPC) + { + const char* c_szName; + if (CPythonNonPlayer::Instance().GetName(kNetActorData.m_dwRace, &c_szName)) + kNetActorData.m_stName = c_szName; + else + kNetActorData.m_stName = chrInfoPacket.name; + } + else + { + kNetActorData.m_stName = chrInfoPacket.name; + } kNetActorData.m_dwGuildID = chrInfoPacket.dwGuildID; kNetActorData.m_dwLevel = chrInfoPacket.dwLevel; kNetActorData.m_sAlignment=chrInfoPacket.sAlignment; From 9eea8d9bc08fc2c8d6f75e94dbae1a652962f20a Mon Sep 17 00:00:00 2001 From: rtw1x1 Date: Wed, 21 Jan 2026 01:51:43 +0000 Subject: [PATCH 3/3] ML-Client: Item Names client sided --- src/UserInterface/Packet.h | 11 ++ src/UserInterface/PythonNetworkStream.cpp | 1 + src/UserInterface/PythonNetworkStream.h | 2 + .../PythonNetworkStreamPhaseGame.cpp | 112 ++++++++++++++++++ .../PythonNetworkStreamPhaseGameItem.cpp | 35 ++++++ 5 files changed, 161 insertions(+) diff --git a/src/UserInterface/Packet.h b/src/UserInterface/Packet.h index 51864d4..53bf1a9 100644 --- a/src/UserInterface/Packet.h +++ b/src/UserInterface/Packet.h @@ -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; diff --git a/src/UserInterface/PythonNetworkStream.cpp b/src/UserInterface/PythonNetworkStream.cpp index 1d3d590..a2eb607 100644 --- a/src/UserInterface/PythonNetworkStream.cpp +++ b/src/UserInterface/PythonNetworkStream.cpp @@ -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)); diff --git a/src/UserInterface/PythonNetworkStream.h b/src/UserInterface/PythonNetworkStream.h index c5d30e7..6fc01f7 100644 --- a/src/UserInterface/PythonNetworkStream.h +++ b/src/UserInterface/PythonNetworkStream.h @@ -447,6 +447,7 @@ class CPythonNetworkStream : public CNetworkStream, public CSingleton= '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; diff --git a/src/UserInterface/PythonNetworkStreamPhaseGameItem.cpp b/src/UserInterface/PythonNetworkStreamPhaseGameItem.cpp index ee32244..134319c 100644 --- a/src/UserInterface/PythonNetworkStreamPhaseGameItem.cpp +++ b/src/UserInterface/PythonNetworkStreamPhaseGameItem.cpp @@ -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() {