This does two things. The first is fixing a minor problem in Client::Handle_OP_AdventureMerchantRequest(...), the strn0cpy was overwriting the last character of the string, since .size() doesn't include the 0 terminator.
zone\client_packet.cpp
Code:
Index: client_packet.cpp
===================================================================
--- client_packet.cpp (revision 1944)
+++ client_packet.cpp (working copy)
@@ -2228,7 +2228,7 @@
//^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap
EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse,ss.str().size()+2);
outapp->pBuffer[0] = count;
- strn0cpy((char*)&outapp->pBuffer[1],ss.str().c_str(),ss.str().size());
+ strn0cpy((char*)&outapp->pBuffer[1],ss.str().c_str(),ss.str().size()+1);
FastQueuePacket(&outapp);
}
The second problem is that the current SQL query to load the merchantlists for the whole zone returns duplicates of a merchant list for every NPC that uses it. Regular merchants seem to catch this during the slot integration with the tmp list, but for LDoN Adventure Merchants, who generally come two to a camp, this leads to a complete repeat of their inventory. The only reason it doesn't fully show everything twice for the client is the cutoff point of 255 entries.
I feel I have to offer two possible solutions for this. One is a version that just fixes the SQL to weed out the duplicates. While not hugely so, this does slow down the query a bit though. (Generally something like 0.01 to 0.02 sec on the old computer I use for a server test environment.) This falls about within in the same range as using the full 'group by ... order by ...' bit that was commented out in the code though as being very slow, hence the second option of doing the dupe weeding in the code.
zone\zone.cpp - SQL version
Code:
Index: zone.cpp
===================================================================
--- zone.cpp (revision 1944)
+++ zone.cpp (working copy)
@@ -578,7 +578,7 @@
"from merchantlist ml, npc_types nt, spawnentry se, spawn2 s2 "
"where nt.merchant_id=ml.merchantid and nt.id=se.npcid "
"and se.spawngroupid=s2.spawngroupid and s2.zone='%s' and s2.version=%u "
- //"group by ml.merchantid,slot order by merchantid,slot asc" //this made the query use a temp table/filesort (very slow)... so we handle unsorted data on our end.
+ "group by ml.merchantid, ml.slot"
, GetShortName(), GetInstanceVersion()));
if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) {
safe_delete(dbaw);
zone\zone.cpp - code based
Code:
Index: zone.cpp
===================================================================
--- zone.cpp (revision 1944)
+++ zone.cpp (working copy)
@@ -544,6 +544,8 @@
void Zone::LoadMerchantData_result(MYSQL_RES* result) {
MYSQL_ROW row;
std::map<uint32,std::list<MerchantList> >::iterator cur;
+ std::list<MerchantList>::iterator dupes;
+ bool found;
int32 npcid = 0;
while((row = mysql_fetch_row(result))) {
MerchantList ml;
@@ -559,7 +561,17 @@
}
ml.slot = atoul(row[1]);
ml.item = atoul(row[2]);
- cur->second.push_back(ml);
+ // Need to check for duplicate entries since we get the merchantlist
+ // repeated for every npc in the zone that uses it.
+ found = false;
+ for (dupes = cur->second.begin(); dupes != cur->second.end() && !found; dupes++) {
+ MerchantList mertemp = *dupes;
+ if (mertemp.slot == ml.slot && mertemp.item == ml.item) {
+ found = true;
+ }
+ }
+ if (!found)
+ cur->second.push_back(ml);
}
//mysql_free_result(result);
// LogFile->write(EQEMuLog::Status, "Finished Loading Merchant Lists...");
Either works to solve the problem. I'm personally not seeing the problem with the SQL version, even the bit that was commented out, but given how often the DB seems to end up being the bottle-neck I've felt it better to provide the alternative.