Mod protocol
(copy past some code.) |
(→process modinfo packet) |
||
Line 343: | Line 343: | ||
− | + | == proces mod data example == | |
Line 365: | Line 365: | ||
break; | break; | ||
</pre> | </pre> | ||
+ | <pre> | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize) | bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize) | ||
Line 622: | Line 623: | ||
// NEO: NMP END <-- Xanatos -- | // NEO: NMP END <-- Xanatos -- | ||
</pre> | </pre> | ||
− | |||
== backward compatibility == | == backward compatibility == |
Revision as of 20:59, 9 July 2007
Contents |
Under construction
THIS IS NOT FINAL.. feel free to edit.
Introduction
Modprot is needed for more clean protocol extensions by eMule modders....
Protocol
Hello
Set the modbit ot in in CT_EMULE_MISCOPTIONS2
in baseclient.cpp, void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data)
// eMule Misc. Options #2 const UINT uKadVersion = KADEMLIA_VERSION; const UINT uSupportLargeFiles = 1; const UINT uExtMultiPacket = 1; const UINT uReserved = 1; // mod bit const UINT uSupportsCryptLayer = thePrefs.IsClientCryptLayerSupported() ? 1 : 0; const UINT uRequestsCryptLayer = thePrefs.IsClientCryptLayerRequested() ? 1 : 0; const UINT uRequiresCryptLayer = thePrefs.IsClientCryptLayerRequired() ? 1 : 0; const UINT uSupportsSourceEx2 = 1; CTag tagMisOptions2(CT_EMULE_MISCOPTIONS2, // (RESERVED ) (uSupportsSourceEx2 << 10) | (uRequiresCryptLayer << 9) | (uRequestsCryptLayer << 8) | (uSupportsCryptLayer << 7) | (uReserved << 6) | (uExtMultiPacket << 5) | (uSupportLargeFiles << 4) | (uKadVersion << 0) ); tagMisOptions2.WriteTagToFile(data);
Process hello.
baseclient.cpp bool CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data)
case CT_EMULE_MISCOPTIONS2: // 21 Reserved // 1 Supports SourceExachnge2 Packets, ignores SX1 Packet Version // 1 Requires CryptLayer // 1 Requests CryptLayer // 1 Supports CryptLayer // 1 Reserved (ModBit) // 1 Ext Multipacket (Hash+Size instead of Hash) // 1 Large Files (includes support for 64bit tags) // 4 Kad Version if (temptag.IsInt()) { m_fSupportsSourceEx2 = (temptag.GetInt() >> 10) & 0x01; m_fRequiresCryptLayer = (temptag.GetInt() >> 9) & 0x01; m_fRequestsCryptLayer = (temptag.GetInt() >> 8) & 0x01; m_fSupportsCryptLayer = (temptag.GetInt() >> 7) & 0x01; m_NeoModProtVersion = (temptag.GetInt() >> 6) & 0x01; // modbit NEO: NMP - [NeoModProt] <-- Xanatos -- m_fExtMultiPacket = (temptag.GetInt() >> 5) & 0x01; m_fSupportsLargeFiles = (temptag.GetInt() >> 4) & 0x01; m_byKadVersion = (uint8)((temptag.GetInt() >> 0) & 0x0f); dwEmuleTags |= 8; if (bDbgInfo) m_strHelloInfo.AppendFormat(_T("\n KadVersion=%u, LargeFiles=%u ExtMultiPacket=%u CryptLayerSupport=%u CryptLayerRequest=%u CryptLayerRequires=%u m_fSupportsSourceEx2=%u"), m_byKadVersion, m_fSupportsLargeFiles, m_fExtMultiPacket, m_fSupportsCryptLayer, m_fRequestsCryptLayer, m_fRequiresCryptLayer, m_fSupportsSourceEx2); m_fRequestsCryptLayer &= m_fSupportsCryptLayer; m_fRequiresCryptLayer &= m_fRequestsCryptLayer;
==Send hello answer and modprot info ==
Listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode)
if (client->GetHashType() == SO_EMULE && !bIsMuleHello) client->SendMuleInfoPacket(false); client->SendHelloAnswer(); if (client) { // NEO: NMP - [NeoModProt] -- Xanatos --> if(client->GetNeoModProtVersion()) client->SendModInfoPacket();
Example of sendmoinfopacket:
void CUpDownClient::SendModInfoPacket(){ if (socket == NULL){ ASSERT(0); return; } CSafeMemFile data(128); uint32 tagcount=2+1; #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] if(theApp.IsFirewalled() && Kademlia::CKademlia::IsConnected()) tagcount += 1; #endif //NATTUNNELING // NEO: NATT END data.WriteUInt32(tagcount); // nr. of tags /************************************************************************************ * Note: The meaning of the bit fields in the current sample * * have to be reworked for the final release version numbers are almost useless, * * support (or newver version) should be anounced only by single bit flags * ************************************************************************************/ #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] const UINT uNatT = NeoPrefs.IsNatTraversalEnabled(); #endif //NATTUNNELING // NEO: NATT END uint32 uNeoFeatures = // ((0 & 0xff) << 24) | // Unused // ((0 & 0x0f) << 20) | // Reserved #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] // ((0 & 0x03) << 18) | // Reserved for nat-t ((1 & 0x01) << 17) | // NEO: RTP - [ReuseTCPPort] ((uNatT & 0x01) << 16) | // Support Nat Traversal // ((0 & 0x01) << 15) | // NEO: XSB - [XSBuddy] #endif //NATTUNNELING // NEO: NATT END ((1 & 0x07) << 12) | // NEO: NXS - [NeoXS] // ((0 & 0x03) << 10) | // Reserved ((1 & 0x01) << 9 ) | // LowID UDP Ping Support (notifyes a fix form xman1 that allow the remote low ID to use udp reasks) // NEO: MOD - [NewUploadState] ((1 & 0x01) << 8 ) | // Unsolicited Part Status (allows our client ro recieve filestatus at any time) // NEO: USPS - [UnSolicitedPartStatus] // ((0 & 0x0f) << 4 ) | // Reserved ((0 & 0x0f) << 0 ); CTag tagNeoModProt(CT_NEO_FEATURES, uNeoFeatures); tagNeoModProt.WriteTagToFile(&data); uint32 nSCT = NeoPrefs.UseSubChunkTransfer() ? 1 : 0; // version 4 bit uint32 ICSv2 = NeoPrefs.UseIncompletePartStatus() ? 2 : 0; // version 3 bit // X-ToDo: //uint32 RPS = NeoPrefs.UseRealPartStatus() ? 1 : 0; // version 3 bit //uint32 HPS = NeoPrefs.UseRealPartStatus() ? 1 : 0; // flag 1 bit //uint32 BPS = NeoPrefs.UseRealPartStatus() ? 1 : 0; // flag 1 bit uint32 uFileStatus = // ((0 & 0xff) << 24) | // unused // ((0 & 0xff) << 16) | // reserved // ((BPS & 0x01) << 15) | // RPS Flag Blocked Parts // ((HPS & 0x01) << 14) | // RPS Flag Hiden Parts // ((RPS & 0x07) << 11) | // RPS VErsion ((ICSv2 & 0x07) << 8 ) | // ICSv2 Version // ((0 & 0x0f) << 4 ) | // reserved ((nSCT & 0x0f) << 0 ); // SCT version CTag tagPartStatus(CT_EXT_FILESTATUS,uFileStatus); tagPartStatus.WriteNewEd2kTag(&data); #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] if(theApp.IsFirewalled() && Kademlia::CKademlia::IsConnected()) { // Note: to connect a cleint over his buddy or to request a calback we need not only his buddy IP/Port but also his Buddy ID // So to get this realy to work we need to transmitt the buddy ID to Kademlia::CUInt128 uBuddyID(true); uBuddyID.Xor(Kademlia::CKademlia::GetPrefs()->GetKadID()); byte cID[16]; uBuddyID.ToByteArray(cID); CTag tagBuddyID(CT_EMULE_BUDDYID, cID); tagBuddyID.WriteNewEd2kTag(&data); } #endif //NATTUNNELING // NEO: NATT END // Named tag test begin LPCTSTR FunnyStr[3]; FunnyStr[0] = _T("Vote for the Pirate Party of your country"); FunnyStr[1] = _T("Have a nice day ;)"); FunnyStr[2] = _T("Confucius says: If you dont upload 10 GB/day good will punish you"); CTag tagMyMail("Mod:Hello",FunnyStr[rand()%ARRSIZE(FunnyStr)]); tagMyMail.WriteNewEd2kTag(&data); // Named tag test end Packet* packet = new Packet(&data,OP_MODPROT); packet->opcode = OP_MODINFOPACKET; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__ModInfoPacket", this); theStats.AddUpDataOverheadOther(packet->size); socket->SendPacket(packet,true,true); }
process modinfo packet
listensocket.cpp
bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize) { try { try { if (!client) { theStats.AddDownDataOverheadOther(uRawSize); throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION); } if (thePrefs.m_iDbgHeap >= 2) ASSERT_VALID(client); switch(opcode) { case OP_MODINFOPACKET: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP__ModInfoPacket", client); theStats.AddDownDataOverheadOther(uRawSize); if(client->GetNeoModProtVersion() == 0) throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!")); client->ProcessModInfoPacket(packet,size); // Now after we have all informations about the cleint the connection is established client->ConnectionEstablished(); // start secure identification, if // - we have received OP_MODINFOPACKET, (Neo Compatible new eMule mods) if (client->GetInfoPacketsReceived() == IP_BOTH) client->InfoPacketsReceived(); break; } // NEO: NMPm - [NeoModProtMultiPacket] case OP_MODMULTIPACKET: { ...
baseclient.cpp
void CUpDownClient::ProcessModInfoPacket(const uchar* pachPacket, uint32 nSize) { bool bDbgInfo = thePrefs.GetUseDebugDevice(); CString m_strModInfo; CSafeMemFile data(pachPacket, nSize); uint32 tagcount = data.ReadUInt32(); if (bDbgInfo) m_strModInfo.AppendFormat(_T(" Tags=%u"), (UINT)tagcount); for (uint32 i = 0; i < tagcount; i++) { CTag temptag(&data, false); switch (temptag.GetNameID()) { case CT_NEO_FEATURES: if (temptag.IsInt()){ // = (uint8)(temptag.GetInt() >> 24) & 0xff; // = (uint8)(temptag.GetInt() >> 20) & 0x0f; #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] // = (uint8)(temptag.GetInt() >> 18) & 0x03; m_PublicPortReport = (uint8)(temptag.GetInt() >> 17) & 0x01; // NEO: RTP - [ReuseTCPPort] m_SupportsNatTraversal = (uint8)(temptag.GetInt() >> 16) & 0x01; // = (uint8)(temptag.GetInt() >> 15) & 0x01; // NEO: XSB - [XSBuddy] #endif //NATTUNNELING // NEO: NATT END m_NeoXSVersion = (uint8)(temptag.GetInt() >> 12) & 0x07; // NEO: NXS - [NeoXS] // = (uint8)(temptag.GetInt() >> 10) & 0x03; m_LowIDUDPPingSupport = (uint8)(temptag.GetInt() >> 9 ) & 0x01; m_UnsolicitedPartStatus = (uint8)(temptag.GetInt() >> 8 ) & 0x01; // = (uint8)(temptag.GetInt() >> 4 ) & 0x0f; // = (uint8)(temptag.GetInt() >> 0 ) & 0x0f; }else if (bDbgInfo) m_strHelloInfo.AppendFormat(_T("\n ***UnkType=%s"), temptag.GetFullInfo()); break; case CT_EXT_FILESTATUS: if (temptag.IsInt()){ // X-ToDo // = (uint8)(temptag.GetInt() >> 24) & 0xff; // = (uint8)(temptag.GetInt() >> 16) & 0xff; //uint8 BlockedPartStatus = (uint8)(temptag.GetInt() >> 15) & 0x01; //uint8 HidenPartStatus = (uint8)(temptag.GetInt() >> 14) & 0x01; //uint8 RealPartStatusVer = (uint8)(temptag.GetInt() >> 11) & 0x07; m_IncompletePartVer = (uint8)(temptag.GetInt() >> 8 ) & 0x07; // = (uint8)(temptag.GetInt() >> 4 ) & 0x0f; m_SubChunksVer = (uint8)(temptag.GetInt() >> 0 ) & 0x0f; //ASSERT(BlockedPartStatus || HidenPartStatus || !RealPartStatusVer); // Note: RPC version without one or booth RPS flags is invalid, // the cleint must always say wha is he doint gith the parts 'blocking and hiding' or 'only hiding', // or he can booth depanding on the situation // Booth RPS versions have always teh same version as the writing functions are identical // just other opcodes are used and other part states notifyed //if(BlockedPartStatus) // m_BlockedPartStatusVer = RealPartStatusVer; //if(HidenPartStatus) // m_HidenPartStatusVer = RealPartStatusVer; }else if (bDbgInfo) m_strModInfo.AppendFormat(_T("\n ***UnkType=%s"), temptag.GetFullInfo()); break; #ifdef NATTUNNELING // NEO: NATT - [NatTraversal] case CT_EMULE_BUDDYID: if(temptag.IsHash()) { SetBuddyID(temptag.GetHash()); } else{ if (bDbgInfo) m_strModInfo.AppendFormat(_T("\n ***UnkType=%s"), temptag.GetFullInfo()); } break; #endif //NATTUNNELING // NEO: NATT END default: // Named tag test begin if(strcmp(temptag.GetName(),"Mod:Hello") == 0) { if(temptag.IsStr()) Log(LOG_SUCCESS,_T("*** Greathings from %s: %s"),GetUserName(),temptag.GetStr()); } else // Named tag test end if (bDbgInfo) m_strModInfo.AppendFormat(_T("\n ***UnkTag=%s"), temptag.GetFullInfo()); } }
proces mod data example
listensocket.cpp bool CClientReqSocket::PacketReceivedCppEH(Packet* packet)
// NEO: NMP - [NeoModProt] -- Xanatos --> case OP_MODPACKEDPROT: if (!packet->UnPackPacket()){ if (thePrefs.GetVerbose()) AddDebugLogLine(false, _T("Failed to decompress client Mod TCP packet; %s; %s"), DbgGetClientTCPPacket(packet->prot, packet->opcode, packet->size), DbgGetClientInfo()); bResult = false; break; } case OP_MODPROT: bResult = ProcessModPacket((const BYTE*)packet->pBuffer, packet->size, packet->opcode, uRawSize); break;
// NEO: NMP - [NeoModProt] -- Xanatos --> bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize) { try { try { if (!client) { theStats.AddDownDataOverheadOther(uRawSize); throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION); } if (thePrefs.m_iDbgHeap >= 2) ASSERT_VALID(client); switch(opcode) { case OP_MODINFOPACKET: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP__ModInfoPacket", client); theStats.AddDownDataOverheadOther(uRawSize); if(client->GetNeoModProtVersion() == 0) throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!")); client->ProcessModInfoPacket(packet,size); // Now after we have all informations about the cleint the connection is established client->ConnectionEstablished(); // start secure identification, if // - we have received OP_MODINFOPACKET, (Neo Compatible new eMule mods) if (client->GetInfoPacketsReceived() == IP_BOTH) client->InfoPacketsReceived(); break; } // NEO: NMPm - [NeoModProtMultiPacket] case OP_MODMULTIPACKET: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_ModMultiPacket", client, (size >= 24) ? packet : NULL); theStats.AddDownDataOverheadFileRequest(uRawSize); client->CheckHandshakeFinished(); CSafeMemFile data_in(packet, size); uchar reqfilehash[16]; data_in.ReadHash16(reqfilehash); CKnownFile* reqfile; if ( (reqfile = theApp.sharedfiles->GetFileByID(reqfilehash)) == NULL ){ if ( !((reqfile = theApp.downloadqueue->GetFileByID(reqfilehash)) != NULL && reqfile->GetFileSize() > (uint64)PARTSIZE) ){ client->CheckFailedFileIdReqs(reqfilehash); break; } } CClientFileStatus* status = client->GetFileStatus(reqfile, true); // NEO: SCFS - [SmartClientFileStatus] client->ReadModMultiPacket(&data_in,reqfile,status); // NEO: BPS - [BetterPassiveSourceFinding] //if(reqfile->IsPartFile() && !(client->GetDownloadState()==DS_ONQUEUE && reqfile==client->GetRequestFile())) // client->ProcessModFileStatus(true, status, (CPartFile*)reqfile, true); // NEO: SCFS - [SmartClientFileStatus] // NEO: BPS END ///////////////////// answer CSafeMemFile data_out(128); data_out.WriteHash16(reqfile->GetFileHash()); client->WriteModMultiPacket(&data_out, reqfile, status, false); if (data_out.GetLength() > 16) { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__ModMultiPacketAns", client, reqfile->GetFileHash()); Packet* reply = new Packet(&data_out, OP_MODPROT); reply->opcode = OP_MODMULTIPACKETANSWER; theStats.AddUpDataOverheadFileRequest(reply->size); SendPacket(reply, true); } break; } case OP_MODMULTIPACKETANSWER: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_ModMultiPacketAns", client, (size >= 16) ? packet : NULL); theStats.AddDownDataOverheadFileRequest(uRawSize); client->CheckHandshakeFinished(); CSafeMemFile data_in(packet, size); uchar reqfilehash[16]; data_in.ReadHash16(reqfilehash); CPartFile* reqfile = theApp.downloadqueue->GetFileByID(reqfilehash); //Make sure we are downloading this file. if (reqfile==NULL){ client->CheckFailedFileIdReqs(reqfilehash); throw GetResString(IDS_ERR_WRONGFILEID) + _T(" (OP_MODMULTIPACKETANSWER; reqfile==NULL)"); } if (client->GetRequestFile()==NULL) throw GetResString(IDS_ERR_WRONGFILEID) + _T(" (OP_MODMULTIPACKETANSWER; client->GetRequestFile()==NULL)"); if (reqfile != client->GetRequestFile()) throw GetResString(IDS_ERR_WRONGFILEID) + _T(" (OP_MODMULTIPACKETANSWER; reqfile!=client->GetRequestFile())"); CClientFileStatus* status = client->GetFileStatus(reqfile, true); // NEO: SCFS - [SmartClientFileStatus] client->ReadModMultiPacket(&data_in,reqfile,status); client->ProcessModFileStatus(false, status, reqfile); break; } // NEO: NMPm END // NEO: SCT - [SubChunkTransfer] case OP_SUBCHUNKMAPS: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_SubChunkMaps", client, (size >= 16) ? packet : NULL); theStats.AddDownDataOverheadFileRequest(size); CSafeMemFile data(packet, size); uchar cfilehash[16]; data.ReadHash16(cfilehash); CPartFile* reqfile = theApp.downloadqueue->GetFileByID(cfilehash); if (reqfile == NULL){ client->CheckFailedFileIdReqs(cfilehash); break; } CClientFileStatus* status = client->GetFileStatus(reqfile, true); status->ReadSubChunkMaps(&data); if(client->GetDownloadState() == DS_NONEEDEDPARTS) // the file must be NNP or we will perform a secund unned ask client->ProcessModFileStatus(false, status, reqfile); break; } // NEO: SCT END case OP_FILEINCSTATUS: //case OP_FILEHIDENSTATUS: //case OP_FILEBLOCKEDSTATUS: { EPartStatus type = CFS_Normal; switch(opcode){ // NEO: ICS - [InteligentChunkSelection] case OP_FILEINCSTATUS: type = CFS_Incomplete; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_FileImcompleteStatus", client, (size >= 16) ? packet : NULL); break; // NEO: ICS END // NEO: RPS - [RealPartStatus] /*case OP_FILEHIDENSTATUS: type = CFS_Hiden; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_FileHidenStatus", client, (size >= 16) ? packet : NULL); break; case OP_FILEBLOCKEDSTATUS: type = CFS_Blocked; if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_FileBlockedStatus", client, (size >= 16) ? packet : NULL); break;*/ // NEO: RPS END } theStats.AddDownDataOverheadFileRequest(size); CSafeMemFile data(packet, size); uchar cfilehash[16]; data.ReadHash16(cfilehash); CKnownFile* reqfile = theApp.sharedfiles->GetFileByID(cfilehash); if(reqfile == NULL) reqfile = (CKnownFile*)theApp.downloadqueue->GetFileByID(cfilehash); if (reqfile == NULL){ client->CheckFailedFileIdReqs(cfilehash); break; } CClientFileStatus* status = client->GetFileStatus(reqfile, true); status->ReadFileStatus(&data, type); reqfile->UpdatePartsInfoEx(type); //if(reqfile->IsPartFile() && client->GetDownloadState() == DS_NONEEDEDPARTS && opcode == OP_FILEHIDENSTATUS) // only hidden status is interresant // client->ProcessModFileStatus(false, status, (CPartFile*)reqfile, false, false, true); break; } #ifdef NATTUNNELING // NEO: RTP - [ReuseTCPPort] case OP_PUBLICPORT_ANSWER: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_PublicPortAns", client); theStats.AddDownDataOverheadOther(uRawSize); client->ProcessPublicPortAnswer(packet, size); break; } case OP_PUBLICPORT_REQ: { if (thePrefs.GetDebugClientTCPLevel() > 0) DebugRecv("OP_PublicPortReq", client); theStats.AddDownDataOverheadOther(uRawSize); if (thePrefs.GetDebugClientTCPLevel() > 0) DebugSend("OP__PublicPortAns", client); Packet* pPacket = new Packet(OP_PUBLICPORT_ANSWER, 2, OP_MODPROT); SOCKADDR_IN sockAddr = {0}; int nSockAddrLen = sizeof(sockAddr); GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen); uint16 Port = ntohs(sockAddr.sin_port); PokeUInt16(pPacket->pBuffer, Port); theStats.AddUpDataOverheadOther(pPacket->size); SendPacket(pPacket); break; } #endif //NATTUNNELING // NEO: RTP END // NEO: NXS - [NeoXS] case OP_ANSWERSOURCES: ProcessExtPacket(packet, size, opcode, uRawSize); // we use the same propacket processor as for the official XS break; // NEO: NXS END default: theStats.AddDownDataOverheadOther(uRawSize); PacketToDebugLogLine(_T("ModProt"), packet, size, opcode); } } catch(CFileException* error) { error->Delete(); throw GetResString(IDS_ERR_INVALIDPACKAGE); } catch(CMemoryException* error) { error->Delete(); throw CString(_T("Memory exception")); } } catch(CString error) { if (thePrefs.GetVerbose() && !error.IsEmpty()) DebugLogWarning(_T("Error: %s - while processing Mod packet: opcode=%s size=%u; %s"), error, DbgGetMuleClientTCPOpcode(opcode), size, DbgGetClientInfo()); // Note: don't disconnect cleints on mod prot errors, the extensions are just addons and if thay fail the client will work anyway //if (client) // client->SetDownloadState(DS_ERROR, _T("ProcessModPacket error. ") + error); //Disconnect(_T("ProcessModPacket error. ") + error); //return false; } return true; } // NEO: NMP END <-- Xanatos --
backward compatibility
Modstring is still send and replied in hello packet to maintain backward compatibility However a opcode for modstring will be defined for those who alos want to move it to the mod protocol