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

Development::Bots Forum for bots.

Closed Thread
 
Thread Tools Display Modes
  #1  
Old 09-28-2008, 08:41 PM
spider661
Discordant
 
Join Date: Oct 2005
Location: michigain
Posts: 260
Default

ok i found the problem i was having with groups not sticking after zoneing its in my bot code can anyone see if they can help me fix it? what happens is they zone and stay in group but it seems its clearing them from the database or the group itself.. then when they zone back into the zone it kills the group and everyone has to reform.

Code:
void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {

#ifdef EQBOTS

	// EQoffline: Remove the group if the leader is grouped
	Mob *clientmob = CastToMob();
	if(clientmob) {
		int16 cmid = clientmob->GetID();
		if(clientmob->IsBotRaiding()) {
			BotRaids* br = entity_list.GetBotRaidByMob(clientmob);
			if(br) {
				br->RemoveRaidBots();
				br = NULL;
			}
		}
		if(clientmob->IsGrouped()) {
			Group *g = entity_list.GetGroupByMob(clientmob);
			if(g) {
				for(int i=5; i>=0; i--) {
					if(g->members[i] && g->members[i]->IsBot()) {
						g->members[i]->Kill();
					}
				}
				if(g->BotGroupCount() <= 1) {
					g->DisbandGroup();
				}
			}
		}
		database.CleanBotLeader(cmid);
	}

#endif //EQBOTS

	zoning = true;
	if (app->size != sizeof(ZoneChange_Struct)) {
		LogFile->write(EQEMuLog::Debug, "Wrong size: OP_ZoneChange, size=%d, expected %d", app->size, sizeof(ZoneChange_Struct));
		return;
	}

#if EQDEBUG >= 5
	LogFile->write(EQEMuLog::Debug, "Zone request from %s", GetName());
	DumpPacket(app);
#endif
	ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer;

	uint16 target_zone_id = 0;
	ZonePoint* zone_point = NULL;

	//figure out where they are going.
	if(zc->zoneID == 0) {
		//client dosent know where they are going...
		//try to figure it out for them.
		
		switch(zone_mode) {
		case EvacToSafeCoords:
		case ZoneToSafeCoords:
			//going to safe coords, but client dosent know where?
			//assume it is this zone for now.
			cheat_timer.Start(35000,false);
			target_zone_id = zone->GetZoneID();
			break;
		case GMSummon:
			target_zone_id = zonesummon_id;
			break;
		case GateToBindPoint:
			target_zone_id = m_pp.binds[0].zoneId;
			break;
		case ZoneToBindPoint:
			target_zone_id = m_pp.binds[0].zoneId;
			break;
		case ZoneSolicited:  //we told the client to zone somewhere, so we know where they are going.
			cheat_timer.Start(35000,false);
			target_zone_id = zonesummon_id;
			break;
		case ZoneUnsolicited:   //client came up with this on its own.
			zone_point = zone->GetClosestZonePointWithoutZone(GetX(), GetY(), GetZ(), ZONEPOINT_NOZONE_RANGE);
			if(zone_point) {
				//we found a zone point, which is a reasonable distance away
				//assume that is the one were going with.
				target_zone_id = zone_point->target_zone_id;
			} else {
				//unable to find a zone point... is there anything else
				//that can be a valid un-zolicited zone request?
				
				CheatDetected(MQZone);
				Message(13, "Invalid unsolicited zone request.");
				LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id);
				SendZoneCancel(zc);
				return;
			}
			break;
		};
	}
	else {
		// This is to allow both 6.2 and Titanium clients to perform a proper zoning of the client when evac/succor
		// WildcardX 27 January 2008
		if(zone_mode == EvacToSafeCoords && zonesummon_id > 0)
			target_zone_id = zonesummon_id;
		else
		target_zone_id = zc->zoneID;
		
		//if we are zoning to a specific zone unsolicied,
		//then until otherwise determined, they must be zoning
		//on a zone line.
		if(zone_mode == ZoneUnsolicited) {
			zone_point = zone->GetClosestZonePoint(GetX(), GetY(), GetZ(), target_zone_id, ZONEPOINT_ZONE_RANGE);
			//if we didnt get a zone point, or its to a different zone,
			//then we assume this is invalid.
			if(!zone_point || zone_point->target_zone_id != target_zone_id) {
				Message(13, "Invalid unsolicited zone request.");
				LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id);
				if ((cheat_timer.GetRemainingTime())<1 || (!cheat_timer.Enabled())){ //Lieka:  Disable MQGate Detector if timer is active.
					CheatDetected(MQGate);
				}					
				SendZoneCancel(zc);
				return;
			}
		}
	}


	//make sure its a valid zone.
	const char *target_zone_name = database.GetZoneName(target_zone_id);
	if(target_zone_name == NULL) {
		//invalid zone...
		Message(13, "Invalid target zone ID.");
		LogFile->write(EQEMuLog::Error, "Zoning %s: Unable to get zone name for zone id '%d'.", GetName(), target_zone_id);
		SendZoneCancel(zc);
		return;
	}

	//load up the safe coords, restrictions, and verify the zone name
	float safe_x, safe_y, safe_z;
	sint16 minstatus = 0;
	int8 minlevel = 0;
	char flag_needed[128];
	if(!database.GetSafePoints(target_zone_name, &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) {
		//invalid zone...
		Message(13, "Invalid target zone while getting safe points.");
		LogFile->write(EQEMuLog::Error, "Zoning %s: Unable to get safe coordinates for zone '%s'.", GetName(), target_zone_name);
		SendZoneCancel(zc);
		return;
	}

#ifdef EMBPERL
		char buf[10];
		snprintf(buf, 9, "%d", target_zone_id);
		buf[9] = '\0';
		((PerlembParser*)parse)->Event(EVENT_ZONE, 0, buf, (NPC*)NULL, this);
#endif

	//handle circumvention of zone restrictions
	//we need the value when creating the outgoing packet as well.
	int8 ignorerestrictions = zonesummon_ignorerestrictions;
	zonesummon_ignorerestrictions = 0;

	float dest_x=0, dest_y=0, dest_z=0, dest_h;
	dest_h = GetHeading();
	switch(zone_mode) {
	case EvacToSafeCoords:
	case ZoneToSafeCoords:
		LogFile->write(EQEMuLog::Debug, "Zoning %s to safe coords (%f,%f,%f) in %s (%d)", GetName(), safe_x, safe_y, safe_z, target_zone_name, target_zone_id);
		dest_x = safe_x;
		dest_y = safe_y;
		dest_z = safe_z;
		break;
	case GMSummon:
		dest_x = zonesummon_x;
		dest_y = zonesummon_y;
		dest_z = zonesummon_z;
		ignorerestrictions = 1;
		break;
	case GateToBindPoint:
		dest_x = m_pp.binds[0].x;
		dest_y = m_pp.binds[0].y;
		dest_z = m_pp.binds[0].z;
		break;
	case ZoneToBindPoint:
		dest_x = m_pp.binds[0].x;
		dest_y = m_pp.binds[0].y;
		dest_z = m_pp.binds[0].z;
		ignorerestrictions = 1;	//can always get to our bind point? seems exploitable
		break;
	case ZoneSolicited:  //we told the client to zone somewhere, so we know where they are going.
		//recycle zonesummon variables
		cheat_timer.Start(3500,false);
		dest_x = zonesummon_x;
		dest_y = zonesummon_y;
		dest_z = zonesummon_z;
		break;
	case ZoneUnsolicited:   //client came up with this on its own.
		//client requested a zoning... what are the cases when this could happen?
		
		//Handle zone point case:
		if(zone_point != NULL) {
			//they are zoning using a valid zone point, figure out coords
			
			//999999 is a placeholder for 'same as where they were from'
			if(zone_point->target_x == 999999)
				dest_x = GetX();
			else
				dest_x = zone_point->target_x;
			if(zone_point->target_y == 999999)
				dest_y = GetY();
			else
				dest_y = zone_point->target_y;
			if(zone_point->target_z == 999999)
				dest_z=GetZ();
			else
				dest_z = zone_point->target_z;
			if(zone_point->target_heading == 999)
				dest_h = GetHeading();
			else
				dest_h = zone_point->target_heading;
			
			break;
		}
		
		//for now, there are no other cases...
		
		//could not find a valid reason for them to be zoning, stop it.
		CheatDetected(MQZone);
		Message(13, "Invalid unsolicited zone request.");
		LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%s'. Not near a zone point.", GetName(), target_zone_name);
		SendZoneCancel(zc);
		return;
	};

	//OK, now we should know where were going...

	//Check some rules first.
	sint8 myerror = 1;		//1 is succes

	//not sure when we would use ZONE_ERROR_NOTREADY

	//enforce min status and level
	if (!ignorerestrictions && (Admin() < minstatus || GetLevel() < minlevel)) {
		cheat_timer.Start(3500,false); //Lieka:  Don't set off warp detector for when a player is moved to the safe-spot for trying to access a zone without the appropriate level or status requirements (i.e. zoning into FearPlane at level 30, etc)
		myerror = ZONE_ERROR_NOEXPERIENCE;
		}
	
	if(!ignorerestrictions && flag_needed[0] != '\0') {
		//the flag needed string is not empty, meaning a flag is required.
		if(Admin() < minStatusToIgnoreZoneFlags && !HasZoneFlag(target_zone_id)) {
			Message(13, "You must have the flag %s to enter this zone.");
			myerror = ZONE_ERROR_NOEXPERIENCE;
			cheat_timer.Start(3500,false);
		}
	}
	
	//Enforce ldon doungeon entrance rules
	if(myerror == 1 && database.IsLDoNDungeon(target_zone_id)
	// && !ignorerestrictions
	) {
		//this zone is an ldon dungeon
		int advid = GetAdventureID();
		if(advid > 0){
			//we are in an adventure... make sure its the right one
			AdventureInfo ai = database.GetAdventureInfo(advid);
			if(target_zone_id != ai.zonedungeonid) {
				Message(13, "You are not allowed to enter this dungeon!");
				LogFile->write(EQEMuLog::Error, "Zoning %s: Not allowed to enter dungeon '%s' (%d). Not in the right adventure.", GetName(), target_zone_name, target_zone_id);
				SendZoneCancel(zc);
				return;
			}
		} else {
			Message(13, "You are not allowed to enter this dungeon!");
			LogFile->write(EQEMuLog::Error, "Zoning %s: Not allowed to enter dungeon '%s' (%d). Not in any adventure.", GetName(), target_zone_name, target_zone_id);
			SendZoneCancel(zc);
			return;
		}
	}

	if(myerror == 1) {
		//we have successfully zoned
		DoZoneSuccess(zc, target_zone_id, dest_x, dest_y, dest_z, dest_h, ignorerestrictions);
	} else {
		LogFile->write(EQEMuLog::Error, "Zoning %s: Rules prevent this char from zoning into '%s'", GetName(), target_zone_name);
		SendZoneError(zc, myerror);
	}
}
the problem is the remove bots call if i comment it out and don't have bots the group stays intact if i have bots it destroys the group on zoneing and leaves the bots in the last zone.

so i cant just comment it out i have to fix it.

any suggestions?
  #2  
Old 09-28-2008, 09:23 PM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

If you have more than one PC in the group, the Bots should be invited last. Maybe that will help with the group issue.

The bots have been coded such that they cannot zone with you because it would crash the zone when I tried. It was easier for me to make it that way than to figure out how to make them zone with you. If you really want to make bots zone with you there's a ton of other zone/teleport/group teleport code that will need to be changed as well. I recommend living with this restriction.
  #3  
Old 09-28-2008, 09:34 PM
spider661
Discordant
 
Join Date: Oct 2005
Location: michigain
Posts: 260
Default

no thats not it.. i don't care if they zone or not.. the problem is this code is making it so when you zone.. with or without a bot. and your grouped. then your group breaks up. i think its removing you from the new group ids database and not adding you back with or without a bot.

example.. your in a group no bots and die.. your still in group but then you zone back to rejoin your group the group is disbanded and you have to reform.. i tried with all the bot code removed and the code for groups works add it back and it breaks.. remove this peace and it works but if you have bots it breaks the group soon as you zone.
  #4  
Old 09-28-2008, 11:16 PM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

ok, thanks for clearing that up... try this and let me know if it works. Just replace the entire ifdef
Code:
#ifdef EQBOTS

	// EQoffline: Remove the group if the leader is grouped
	Mob *clientmob = CastToMob();
	if(clientmob) {
		int16 cmid = GetID();
		if(clientmob->IsBotRaiding()) {
			BotRaids* br = entity_list.GetBotRaidByMob(clientmob);
			if(br) {
				br->RemoveRaidBots();
				br = NULL;
			}
		}
		if(clientmob->IsGrouped()) {
			Group *g = entity_list.GetGroupByMob(clientmob);
			if(g) {
				bool hasBots = false;
				for(int i=5; i>=0; i--) {
					if(g->members[i] && g->members[i]->IsBot()) {
						hasBots = true;
						g->members[i]->Kill();
					}
				}
				if(hasBots) {
					if(g->BotGroupCount() <= 1) {
						g->DisbandGroup();
					}
				}
			}
		}
		database.CleanBotLeader(cmid);
	}

#endif //EQBOTS
  #5  
Old 09-29-2008, 12:11 AM
spider661
Discordant
 
Join Date: Oct 2005
Location: michigain
Posts: 260
Default

ok that worked great.. now the bots leave group and group stays in tact now 1 more problem.. the player that owns the bots invites a player.. spawns bots and zones out and back bots gone group intact. so then i respawn the bots and try to invite them. says only leader can invite bots and kills them. but then i log on another char and try to invite them and it lets me..

so basically what happens is now the bots cant find the leader of the group even though the owner is still the group leader according to the game.

any ideals?
  #6  
Old 09-29-2008, 02:33 AM
spider661
Discordant
 
Join Date: Oct 2005
Location: michigain
Posts: 260
Default

i fixed it i just removed the group leader check when inviting bots.. i don't care if anyone in group can invite them leader or not.. and it seems to e working now. but i would still like to see a fix to the problem in case i decide to put leader check back in or if anyone else needs it
  #7  
Old 09-29-2008, 06:59 AM
Angelox
AX Classic Developer
 
Join Date: May 2006
Location: filler
Posts: 2,049
Default

I fixed a few exploits on mine;
for players who make armies of Bots and invite them as they fight and loose bots;
in command.cpp around line 8161;
replace

Code:
	if(!strcasecmp(sep->arg[1], "group") && !strcasecmp(sep->arg[2], "add"))
    {
		if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot())
        {
            c->Message(15, "You must target a bot!");
			return;
		}
With this;
Code:
	if(!strcasecmp(sep->arg[1], "group") && !strcasecmp(sep->arg[2], "add"))
    {
		if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot() || (c->IsEngaged())) //Angelox
        {
            c->Message(15, "You must target a bot and can't be engaged!");
			return;
		}
for players who kill players or any mob with the '#bot raid group create';
in command.cpp around line 8645;
replace
Code:
					c->Message(15, "You must have created your raid and your group must be full before doing that!");
				Mob* kmob = c->GetTarget();
				if(kmob != NULL) {
					kmob->Kill();
				}
With
Code:
				c->Message(15, "You must have created your raid and your group must be full before doing that!");
				Mob* kmob = c->GetTarget();
				if(kmob != NULL) {
					kmob->GetTarget();  //Problem? kmob->Kill() was here b4 Angelox
				}
And finally, Since th '#Bot update as it really doesn't work right (at least for me and what I got it doesn't), and is an exploit for no down time (this full heals the bot), I just quoted it out (I'm content to updates when zoning/re-logging the bot);
command.cpp, line 7202 replace;
Code:
c->Message(15, "#bot update [target] - you must type that command once you gain a level.");
with
Code:
	c->Message(15, "#bot update You must zone or re-log to see updated Bot"); //Angelox: removed for exploit (doesn't work right anyways
and quote out lines 8462 - 8486 (more or less)
Code:
/*	if(!strcasecmp(sep->arg[1], "update")) {
//        // Congdar: add IsEngaged check for exploit to keep bots alive by repeatedly using #bot update.
//	  // Angelox: Disabled it totally as it is an exploit for no down time 
        if(c->GetTarget() != NULL)
        {
            if(c->GetTarget()->IsBot() && (c->GetTarget()->BotOwner == c->CastToMob()) && !c->GetTarget()->IsEngaged()) {
                Mob *bot = c->GetTarget();
                bot->SetLevel(c->GetLevel());
				bot->CalcBotStats();
            }
            else {
				if(c->GetTarget()->IsEngaged()) {
					c->Message(15, "You cannot update while engaged.");
				}
				else {
					c->Message(15, "You must target a bot first");
				}
            }
        }
        else {
			c->Message(15, "You must target a bot first");
        }
        return;
	}
*/
This fixes are tested and running on a few mini-login servers, mad by request of the owners.
  #8  
Old 09-29-2008, 09:05 AM
Angelox
AX Classic Developer
 
Join Date: May 2006
Location: filler
Posts: 2,049
Default

Quote:
Originally Posted by spider661 View Post
i fixed it i just removed the group leader check when inviting bots.. i don't care if anyone in group can invite them leader or not.. and it seems to e working now. but i would still like to see a fix to the problem in case i decide to put leader check back in or if anyone else needs it
The group fix by Congdar is great!
Tell me what you did to remove the group leader check.
  #9  
Old 09-29-2008, 09:42 AM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

Quote:
Originally Posted by spider661 View Post
ok that worked great.. now the bots leave group and group stays in tact now 1 more problem.. the player that owns the bots invites a player.. spawns bots and zones out and back bots gone group intact. so then i respawn the bots and try to invite them. says only leader can invite bots and kills them. but then i log on another char and try to invite them and it lets me..

so basically what happens is now the bots cant find the leader of the group even though the owner is still the group leader according to the game.

any ideals?
This is a bug in group code, and not related to bots. There's a missing opcode for group leader it seems doesn't get updated when zoning. I thought KLS had fixed this but I guess not.
  #10  
Old 09-29-2008, 09:59 AM
Angelox
AX Classic Developer
 
Join Date: May 2006
Location: filler
Posts: 2,049
Default

Quote:
Originally Posted by Congdar View Post
This is a bug in group code, and not related to bots. There's a missing opcode for group leader it seems doesn't get updated when zoning. I thought KLS had fixed this but I guess not.
What if you coded the bots to look in table 'group_leaders' for whoever is the leader.
  #11  
Old 09-29-2008, 10:12 AM
Congdar
Developer
 
Join Date: Jul 2007
Location: my own little world
Posts: 751
Default

Angelox,
Nice fixes... I'll add them to my source, but I'm going to allow the update command but I'll fix it so it only works for what the actual update command was intended for... when you level up, you're supposed to be able to level up your bots with you, so I'll add a level check and that should be good.

The killing npc's and players bug is another nice find but is a good command when used as intended. I'll add code to make sure a bot is the target.

It would be nice to get the actual group code fixed, but using the bot leader's db table is a good workaround until then.
  #12  
Old 09-29-2008, 11:23 AM
Angelox
AX Classic Developer
 
Join Date: May 2006
Location: filler
Posts: 2,049
Default

Well, I got a few more that might interest you;
Selos and bard speed spells should not work in dungeons either, what I did was make up some new 'spell ids';
-=-=-=-=-=
spdat.h around 482 add;
Code:
bool IsSowTypeSpell(int16 spell_id);    //Angelox
bool IsFeralPackSpell(int16 spell_id);  //Angelox
-=--=-=-=-=
spdat.cpp around 708 add;
Code:
bool IsFeralPackSpell(int16 spell_id) {  //Angelox, works in dungeons
	bool Result = false;

	// Feral Pack
	if(spell_id == 4058)
		Result = true;

	return Result;
}

bool IsSowTypeSpell(int16 spell_id) {  //Angelox
	bool Result = false;

// Sow Types		Sow		  WolfForm	  ShareGreatWolf	   GreaterWolfForm	  SharWolf		Bard		     Bard		 EagleSow	
	if((spell_id == 278) || (spell_id == 425) || (spell_id == 3579) || (spell_id == 426) || (spell_id == 428)|| (spell_id == 717) || (spell_id == 2605) || (spell_id == 2517))
		Result = true;

	return Result;
}
I wanted to keep some sow spells in dungeons, but others no. My idea is to get things looking natural, the only group wolf I have available is the Feral Pack In dungeons (it is an LDoN dungeon spell), Thought it was pretty cool to have while working in dungeons. I also have some code (will post later) that enables the Druid to only wolf itself (then Sow everone else), and not the whole group. As when we played on live, most players didn't care to get 'Wolfed' all the time.

Once the above code is added, I then went to botAI.cpp at line 751 'case SpellType_Buff:'
entry, I added these to lines with your levitate check;
Code:
	  (IsSowTypeSpell(AIspells[i].spellid) && !zone->CanCastOutdoor()) ||
	  (IsFeralPackSpell(AIspells[i].spellid) && zone->CanCastOutdoor())) { //Angelox
Now it looks like this;
Code:
	// Put the zone levitate check here since bots are able to bypass the client casting check
	 if(IsEffectInSpell(AIspells[i].spellid, SE_Levitate) && !zone->CanLevitate() ||
	  (IsSowTypeSpell(AIspells[i].spellid) && !zone->CanCastOutdoor()) ||
	  (IsFeralPackSpell(AIspells[i].spellid) && zone->CanCastOutdoor())) { //Angelox
	  break;
}
The outcome of this is, when playing outdoors, you look over to the wolf in the group, and think 'Yep, there's the Druid'(sort of a classic look to a Druid). And when in dungeon, you get the numerous benefits of Feral Pack, which players really liked to have.
The Druid should Wolf first, then Sow for this to work.
And I have disabled all wolf forms but the one 'self only' in npc_spells_entries .
Still if you don't like my Druid wolf idea, the other code works for blocking choice spells in and out of dungeons

One problem needs to be solved with Illusions is, you can't equip a Bot while under the effect. I usually have all the gear ready and quickly hand it over before the bot casts the illusion.
Closed Thread


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 09:03 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 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3