Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #1  
Old 06-19-2011, 04:50 AM
Leere
Sarnak
 
Join Date: Sep 2008
Location: Home
Posts: 31
Default MerchantList Loading and LDoN Merchants

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.
Reply With Quote
  #2  
Old 06-19-2011, 08:54 AM
Leere
Sarnak
 
Join Date: Sep 2008
Location: Home
Posts: 31
Default

Another try to get the diff to post without the tab to space conversion. Sorry about any inconvenience.

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);
 }
sql based duplicate elimination
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);
code based duplicate search
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...");
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 03:58 AM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3