EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Bots (https://www.eqemulator.org/forums/forumdisplay.php?f=676)
-   -   Fix for #bot create when same named PC exists (https://www.eqemulator.org/forums/showthread.php?t=36506)

zippzipp 02-16-2013 06:39 PM

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);


NatedogEZ 02-16-2013 11:34 PM

Nice! This really needed to be fixed

Zamthos 02-19-2013 04:19 AM

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;
}


NatedogEZ 02-19-2013 04:33 AM

Doesn't look like this fix works? It seems to still crash

NatedogEZ 02-19-2013 05:18 AM

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

c0ncrete 02-19-2013 06:24 AM

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;"

zippzipp 02-19-2013 11:09 AM

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;
}


Zamthos 02-19-2013 04:37 PM

Is this fix working? Have you tried it on your server/development server?

zippzipp 02-19-2013 10:14 PM

Not yet. I will verify it tomorrow. I have not had a chance yet.

Zamthos 02-19-2013 10:25 PM

All right, the first fix you gave out actually crashed the entire zone.exe rather than fixing anything. D:

wolfwalkereci 02-19-2013 10:27 PM

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.

Zamthos 02-19-2013 10:33 PM

This fix works, 100% confirmed, Wolfwalkereci?

zippzipp 02-20-2013 10:41 AM

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;
}


Zamthos 02-20-2013 05:30 PM

Good, thanks ZippZipp!

zippzipp 02-22-2013 05:37 PM

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;
                }


NatedogEZ 02-22-2013 09:46 PM

Tested and I can confirm this issue is fixed with those changes! Thanks ZippZipp

Zamthos 02-22-2013 09:47 PM

Thanks ZippZipp! Works 100%!

NatedogEZ 02-24-2013 04:46 AM

Can anyone put this fix into the newest revision?

lerxst2112 02-24-2013 05:52 AM

It'd be easier to do so if an actual diff was posted.

NatedogEZ 07-10-2013 04:03 AM

Bot.cpp
Code:

old        new       
...        ...        @@ -2347,16 +2347,17 @@ bool Bot::IsValidName() {
2347        2347                return Result;
2348        2348        }
2349        2349       
2350                -bool Bot::IsBotNameAvailable(std::string* errorMessage) {
2351                -        bool Result = false;
        2350        +bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) {
        2351        +        bool Result1 = false;
        2352        +        bool Result2 = false;
2352        2353       
2353                -        if(this->GetCleanName()) {
        2354        +        if(botName !="") {
2354        2355                        char* Query = 0;
2355        2356                        char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE];
2356        2357                        MYSQL_RES* DatasetResult;
2357        2358                        MYSQL_ROW DataRow;
2358        2359       
2359                -                if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) {
        2360        +                if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", botName), TempErrorMessageBuffer, &DatasetResult)) {
2360        2361                                *errorMessage = std::string(TempErrorMessageBuffer);
2361        2362                        }
2362        2363                        else {
...        ...        @@ -2368,15 +2369,34 @@ bool Bot::IsBotNameAvailable(std::string* errorMessage) {
2368        2369                                }
2369        2370       
2370        2371                                if(ExistingNameCount == 0)
2371                -                                Result = true;
        2372        +                                Result1 = true;
2372        2373       
2373        2374                                mysql_free_result(DatasetResult);
2374                -                }
2375        2375       
        2376        +                        if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM character_ WHERE name LIKE '%s'", botName), TempErrorMessageBuffer, &DatasetResult)) {
        2377        +                                *errorMessage = std::string(TempErrorMessageBuffer);
        2378        +                        } else {
        2379        +                                uint32 ExistingNameCount = 0;
        2380        +
        2381        +                                while(DataRow = mysql_fetch_row(DatasetResult)) {
        2382        +                                        ExistingNameCount = atoi(DataRow[0]);
        2383        +                                        break;
        2384        +                                }
        2385        +
        2386        +                                if(ExistingNameCount == 0)
        2387        +                                        Result2 = true;
        2388        +
        2389        +                                mysql_free_result(DatasetResult);
        2390        +
        2391        +                        }
        2392        +                }
2376        2393                        safe_delete(Query);
2377        2394                }
2378        2395       
2379                -        return Result;
        2396        +        if(Result1 && Result2)
        2397        +                return true;
        2398        +        else
        2399        +                return false;
2380        2400        }
2381        2401       
2382        2402        bool Bot::Save() {
...        ...        @@ -12182,6 +12202,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
12182        12202                        if(!strcasecmp(sep->arg[5], "female"))
12183        12203                                gender = 1;
12184        12204       
        12205        +                if(!IsBotNameAvailable(sep->arg[2],&TempErrorMessage)) {
        12206        +                        c->Message(0, "The name %s is already being used. Please choose a different name.", sep->arg[2]);
        12207        +                        return;
        12208        +                }
        12209        +
12185        12210                        NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(sep->arg[2]), std::string(), c->GetLevel(), atoi(sep->arg[4]), atoi(sep->arg[3]), gender);
12186        12211                        Bot* NewBot = new Bot(DefaultNPCTypeStruct, c);
12187        12212       
...        ...        @@ -12196,11 +12221,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
12196        12221                                        return;
12197        12222                                }
12198        12223       
12199                -                        if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) {
12200                -                                c->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName());
12201                -                                return;
12202                -                        }
12203                -
12204        12224                                if(!TempErrorMessage.empty()) {
12205        12225                                        c->Message(13, "Database Error: %s", TempErrorMessage.c_str());
12206        12226                                        return;



Bot.h
Code:

old        new       
...        ...        @@ -152,7 +152,7 @@ public:
152        152                // Class Methods
153        153                bool IsValidRaceClassCombo();
154        154                bool IsValidName();
155                -        bool IsBotNameAvailable(std::string* errorMessage);
        155        +        static bool IsBotNameAvailable(char *botName, std::string* errorMessage);
156        156                bool DeleteBot(std::string* errorMessage);
157        157                void Spawn(Client* botCharacterOwner, std::string* errorMessage);
158        158                virtual void SetLevel(uint8 in_level, bool command = false);


questmgr.cpp
Code:

old        new       
...        ...        @@ -2011,6 +2011,11 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
2011        2011                                return false;
2012        2012                        }
2013        2013       
        2014        +                if(Bot::IsBotNameAvailable((char*)name,&TempErrorMessage)) {
        2015        +                        initiator->Message(0, "The name %s is already being used. Please choose a different name.", (char*)name);
        2016        +                        return false;
        2017        +                }
        2018        +
2014        2019                        NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
2015        2020                        Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);
2016        2021       
...        ...        @@ -2026,11 +2031,6 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
2026        2031                                        return false;
2027        2032                                }
2028        2033       
2029                -                        if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) {
2030                -                                initiator->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName());
2031                -                                return false;
2032                -                        }
2033                -
2034        2034                                if(!TempErrorMessage.empty()) {
2035        2035                                        initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str());
2036        2036                                        return false;




If that is the correct format...

Kingly_Krab 01-08-2014 02:43 PM

Can we get this committed? I haven't seen anything in the changelog about committing it, or was it a silent commit?


All times are GMT -4. The time now is 01:15 AM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.