Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Bots

Development::Bots Forum for bots.

Reply
 
Thread Tools Display Modes
  #1  
Old 02-16-2013, 06:39 PM
zippzipp
Fire Beetle
 
Join Date: Dec 2012
Posts: 14
Default Fix for #bot create when same named PC exists

Hello all. I found that when you create a bot that has the same name as an existing player it bugs both the player and the bot out like crazy. I added a check against the character_ table in the database to make sure a player does not already exist with the target name.

Code:
Index: bot.cpp
===================================================================
--- bot.cpp	(revision 2506)
+++ bot.cpp	(working copy)
@@ -2370,6 +2370,26 @@
 				Result = true;
 
 			mysql_free_result(DatasetResult);
+
+
+
+			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
+				*errorMessage = std::string(TempErrorMessageBuffer);
+			}
+			else {
+				uint32 ExistingNameCount = 0;
+
+				while(DataRow = mysql_fetch_row(DatasetResult)) {
+					ExistingNameCount = atoi(DataRow[0]);
+					break;
+				}
+
+				if(ExistingNameCount == 0)
+					Result = true;
+
+				mysql_free_result(DatasetResult);
+
+			}
 		}
 
 		safe_delete(Query);
Reply With Quote
  #2  
Old 02-16-2013, 11:34 PM
NatedogEZ's Avatar
NatedogEZ
Developer
 
Join Date: Dec 2012
Posts: 515
Default

Nice! This really needed to be fixed
Reply With Quote
  #3  
Old 02-19-2013, 04:19 AM
Zamthos
Discordant
 
Join Date: Jan 2013
Posts: 284
Default

So I added your fix, but instead of crashing one zone now, it crashes the entire zone.exe, could you explain how this is supposed to be used? It's currently causing more detriment than anything.

Edit: Code below.

Code:
bool Bot::IsBotNameAvailable(std::string* errorMessage) {
	bool Result = false;

	if(this->GetCleanName()) {
		char* Query = 0;
		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
		MYSQL_RES* DatasetResult;
		MYSQL_ROW DataRow;

		if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
			*errorMessage = std::string(TempErrorMessageBuffer);
		}
		else {
			uint32 ExistingNameCount = 0;

			while(DataRow = mysql_fetch_row(DatasetResult)) {
				ExistingNameCount = atoi(DataRow[0]);
				break;
			}

			if(ExistingNameCount == 0)
				Result = true;

			mysql_free_result(DatasetResult);
		
			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) 
			{
				*errorMessage = std::string(TempErrorMessageBuffer);
			}
			else 
			{
				uint32 ExistingNameCount = 0;

				while(DataRow = mysql_fetch_row(DatasetResult)) 
				{
					ExistingNameCount = atoi(DataRow[0]);
					break;
				}

				if(ExistingNameCount == 0)
					Result = true;

				mysql_free_result(DatasetResult);

			}

		}

		safe_delete(Query);
	}

	return Result;
}
Reply With Quote
  #4  
Old 02-19-2013, 04:33 AM
NatedogEZ's Avatar
NatedogEZ
Developer
 
Join Date: Dec 2012
Posts: 515
Default

Doesn't look like this fix works? It seems to still crash
Reply With Quote
  #5  
Old 02-19-2013, 05:18 AM
NatedogEZ's Avatar
NatedogEZ
Developer
 
Join Date: Dec 2012
Posts: 515
Default

Your check seems to first check the Bot names... if there is no bot name... it sets

Result = True


Then it checks players .. if the player DOES exist it doesn't set the result back to false.



Code:
bool Bot::IsBotNameAvailable(std::string* errorMessage) {
	bool Result = false;
	bool ResultBot = false;
	bool ResultPlayer = false;

	if(this->GetCleanName()) {
		char* Query = 0;
		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
		MYSQL_RES* DatasetResult;
		MYSQL_ROW DataRow;

		if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
			*errorMessage = std::string(TempErrorMessageBuffer);
		}
		else {
			uint32 ExistingNameCount = 0;

			while(DataRow = mysql_fetch_row(DatasetResult)) {
				ExistingNameCount = atoi(DataRow[0]);
				break;
			}

			if(ExistingNameCount == 0)
				ResultBot = true;

			mysql_free_result(DatasetResult);
		}
		
	safe_delete(Query);

			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
				*errorMessage = std::string(TempErrorMessageBuffer);
			}
			else {
				uint32 ExistingNameCountPlayer = 0;

				while(DataRow = mysql_fetch_row(DatasetResult)) {
					ExistingNameCountPlayer = atoi(DataRow[0]);
					break;
				}

				if(ExistingNameCountPlayer == 0)
					ResultPlayer = true;

				mysql_free_result(DatasetResult);

			}

	safe_delete(Query);
		
		if(ResultPlayer == true && ResultBot == true) {
			Result = true;
		}
	}

	return Result;
}

I do not have a bots compile to test this on... but this might be a fix? Not 100% on that one just yet
Reply With Quote
  #6  
Old 02-19-2013, 06:24 AM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 719
Default

there is no reason to use COUNT, or LIKE since there is no wildcard being used, because you're only concerned about a single, exact match. you really don't need to return anything but a 1, since you're not doing anything with the ids. it wouldn't hurt to put LIMIT 1 on the end of the query either.

you can do the whole thing in a single query like so.

"SELECT 1 FROM character_ AS c, vwBotCharacterMobs AS b WHERE '%s' IN (c.name, b.name) LIMIT 1;"
__________________
I muck about @ The Forge.
say(rand 99>49?'try '.('0x'.join '',map{unpack 'H*',chr rand 256}1..2):'incoherent nonsense')while our $Noport=1;
Reply With Quote
  #7  
Old 02-19-2013, 11:09 AM
zippzipp
Fire Beetle
 
Join Date: Dec 2012
Posts: 14
Default

I think there was a slight issue with my diff. There was also a bug related to the return value. I have fixed the code and just posted the entire IsBotNameAvailable function above.

As for why I used LIKE: I was just using the same method used that was already there to check against the vwBotCharacterMobs table. Just trying to keep the code consistant, and it was really easy to just copy and paste what was there. You are quite right c0ncrete what you posted is much cleaner. I will look at cleaning the code up a bit when I get some time.


Code:
bool Bot::IsBotNameAvailable(std::string* errorMessage) {
	bool Result1 = false;
	bool Result2 = false;

	if(this->GetCleanName()) {
		char* Query = 0;
		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
		MYSQL_RES* DatasetResult;
		MYSQL_ROW DataRow;

		if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
			*errorMessage = std::string(TempErrorMessageBuffer);
		}
		else {
			uint32 ExistingNameCount = 0;

			while(DataRow = mysql_fetch_row(DatasetResult)) {
				ExistingNameCount = atoi(DataRow[0]);
				break;
			}

			if(ExistingNameCount == 0)
				Result1 = true;

			mysql_free_result(DatasetResult);



			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
				*errorMessage = std::string(TempErrorMessageBuffer);
			}
			else {
				uint32 ExistingNameCount = 0;

				while(DataRow = mysql_fetch_row(DatasetResult)) {
					ExistingNameCount = atoi(DataRow[0]);
					break;
				}

				if(ExistingNameCount == 0)
					Result2 = true;

				mysql_free_result(DatasetResult);

			}
		}
		safe_delete(Query);
	}

	if(Result1 && Result2)
		return true;
	else
		return false;
}
Reply With Quote
  #8  
Old 02-19-2013, 04:37 PM
Zamthos
Discordant
 
Join Date: Jan 2013
Posts: 284
Default

Is this fix working? Have you tried it on your server/development server?
Reply With Quote
  #9  
Old 02-19-2013, 10:14 PM
zippzipp
Fire Beetle
 
Join Date: Dec 2012
Posts: 14
Default

Not yet. I will verify it tomorrow. I have not had a chance yet.
Reply With Quote
  #10  
Old 02-19-2013, 10:25 PM
Zamthos
Discordant
 
Join Date: Jan 2013
Posts: 284
Default

All right, the first fix you gave out actually crashed the entire zone.exe rather than fixing anything. D:
Reply With Quote
  #11  
Old 02-19-2013, 10:27 PM
wolfwalkereci
Discordant
 
Join Date: Dec 2005
Posts: 435
Default

Thanks zip, shame the other bot servers that already had this fixed never shared how they fixed it. It's been a known issue for some time.
Reply With Quote
  #12  
Old 02-19-2013, 10:33 PM
Zamthos
Discordant
 
Join Date: Jan 2013
Posts: 284
Default

This fix works, 100% confirmed, Wolfwalkereci?
Reply With Quote
  #13  
Old 02-20-2013, 10:41 AM
zippzipp
Fire Beetle
 
Join Date: Dec 2012
Posts: 14
Default

Zam

I just confirmed it. Replace your existing Bot::IsBotNameAvailable function with this one. Sorry about the issue before. The problem before was a bug in the code and also the diff i posted. I wish I could update the code in the main post but wont let me.

Code:
bool Bot::IsBotNameAvailable(std::string* errorMessage) {
	bool Result1 = false;
	bool Result2 = false;

	if(this->GetCleanName()) {
		char* Query = 0;
		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
		MYSQL_RES* DatasetResult;
		MYSQL_ROW DataRow;

		if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
			*errorMessage = std::string(TempErrorMessageBuffer);
		}
		else {
			uint32 ExistingNameCount = 0;

			while(DataRow = mysql_fetch_row(DatasetResult)) {
				ExistingNameCount = atoi(DataRow[0]);
				break;
			}

			if(ExistingNameCount == 0)
				Result1 = true;

			mysql_free_result(DatasetResult);



			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
				*errorMessage = std::string(TempErrorMessageBuffer);
			}
			else {
				uint32 ExistingNameCount = 0;

				while(DataRow = mysql_fetch_row(DatasetResult)) {
					ExistingNameCount = atoi(DataRow[0]);
					break;
				}

				if(ExistingNameCount == 0)
					Result2 = true;

				mysql_free_result(DatasetResult);

			}
		}
		safe_delete(Query);
	}

	if(Result1 && Result2)
		return true;
	else
		return false;
}
Reply With Quote
  #14  
Old 02-20-2013, 05:30 PM
Zamthos
Discordant
 
Join Date: Jan 2013
Posts: 284
Default

Good, thanks ZippZipp!
Reply With Quote
  #15  
Old 02-22-2013, 05:37 PM
zippzipp
Fire Beetle
 
Join Date: Dec 2012
Posts: 14
Default

So it turns out the issue was seeded much deeper than I had realized. While the fixes posted before fixed issues with creating bots with same name, they did not fix the issue of that player with the same name going Linkdead when another user attempted to create a bot with their name.

The problem was rooted in the fact that the function in bot.cpp:

Bot::IsBotNameAvailable is a non-static class function and a Bot object was needed in order to check if the name was valid. Creating that Bot object was crashing people with the same name. What I did was make the IsBotNameAvailable a static class function and pass it the name to check. I then call the function before the new Bot object is created. This fixes the problem of crashing and prevents people from making bots with the same name as PCs.

Code:
// Bot.h
// modify the IsBotNameAvailable function prototype

// REPLACE THIS:
bool IsBotNameAvailable(std::string* errorMessage);
// WITH THIS:
static bool IsBotNameAvailable(char *botName, std::string* errorMessage);
Code:
// Bot.cpp
// modify the IsBotNameAvailable function to work off without a this pointer.

bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) {
	bool Result1 = false;
	bool Result2 = false;

	if(botName !="") {
		char* Query = 0;
		char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
		MYSQL_RES* DatasetResult;
		MYSQL_ROW DataRow;

		if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", botName), TempErrorMessageBuffer, &DatasetResult)) {
			*errorMessage = std::string(TempErrorMessageBuffer);
		}
		else {
			uint32 ExistingNameCount = 0;

			while(DataRow = mysql_fetch_row(DatasetResult)) {
				ExistingNameCount = atoi(DataRow[0]);
				break;
			}

			if(ExistingNameCount == 0)
				Result1 = true;

			mysql_free_result(DatasetResult);

			if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", botName), TempErrorMessageBuffer, &DatasetResult)) {
				*errorMessage = std::string(TempErrorMessageBuffer);
			} else {
				uint32 ExistingNameCount = 0;

				while(DataRow = mysql_fetch_row(DatasetResult)) {
					ExistingNameCount = atoi(DataRow[0]);
					break;
				}

				if(ExistingNameCount == 0)
					Result2 = true;

				mysql_free_result(DatasetResult);

			}
		}
		safe_delete(Query);
	}

	if(Result1 && Result2)
		return true;
	else
		return false;
}
Modify all references to IsBotNameAvailable to call the static function.

Code:
// bot.cpp
// REPLACE:
NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(sep->arg[2]), std::string(), c->GetLevel(), atoi(sep->arg[4]), atoi(sep->arg[3]), gender);
		Bot* NewBot = new Bot(DefaultNPCTypeStruct, c);

// WITH:
if(!IsBotNameAvailable(sep->arg[2],&TempErrorMessage)) {
			c->Message(0, "The name %s is already being used. Please choose a different name.", sep->arg[2]);
			return;
		}

		NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(sep->arg[2]), std::string(), c->GetLevel(), atoi(sep->arg[4]), atoi(sep->arg[3]), gender);
		Bot* NewBot = new Bot(DefaultNPCTypeStruct, c);


// REMOVE THIS:
if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) {
			c->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName());
			return;
		}
ALSO update questmgr.cpp for when a bot is created via quest.
Code:
// questmgr.cpp
// REPLACE:
NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
		Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);

// WITH:
if(Bot::IsBotNameAvailable((char*)name,&TempErrorMessage)) {
			initiator->Message(0, "The name %s is already being used. Please choose a different name.", (char*)name);
			return false;
		}

		NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
		Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);


// REMOVE THIS:
if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) {
			initiator->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName());
			return false;
		}
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 04:55 PM.


 

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