From a7ce66856b6425f7236102419528e5760958f33e Mon Sep 17 00:00:00 2001 From: JJ Date: Sat, 20 Apr 2013 14:04:08 -0400 Subject: [PATCH 01/15] Fix rare case where heals from buffs could be negative. --- changelog.txt | 3 ++ zone/spell_effects.cpp | 72 ++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/changelog.txt b/changelog.txt index cb9c9a6af..8b7df0a4d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/20/2013 == +JJ: Fixed rare case where heals from buffs could go negative. + == 04/12/2013 == Derision: Moved entity_list.Clear() prior to destruction of Perl objects in zone shutdown as I was seeing a segfault due to attempts to call EVENT_HATE_LIST as mobs were being destroyed. diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9b1973438..4c7d4cb98 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1178,7 +1178,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) { Gate(); } - // solar: shadow step is handled by client already, nothing required + // shadow step is handled by client already, nothing required break; } @@ -1189,7 +1189,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif if (spells[spell_id].base[i] == 1) BuffFadeByEffect(SE_Blind); - // solar: handled by client + // handled by client // TODO: blind flag? break; } @@ -1369,7 +1369,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Spin: %d", effect_value); #endif - // solar: the spinning is handled by the client + // the spinning is handled by the client int max_level = spells[spell_id].max[i]; if(max_level == 0) max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit @@ -1383,7 +1383,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } else { - // solar: the spinning is handled by the client + // the spinning is handled by the client // Stun duration is based on the effect_value, not the buff duration(alot don't have buffs) Stun(effect_value); if(!IsClient()) { @@ -2004,7 +2004,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Call Pet"); #endif - // solar: this is cast on self, not on the pet + // this is cast on self, not on the pet if(GetPet() && GetPet()->IsNPC()) { GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading()); @@ -2015,7 +2015,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_StackingCommand_Block: case SE_StackingCommand_Overwrite: { - // solar: these are special effects used by the buff stuff + // these are special effects used by the buff stuff break; } @@ -2100,7 +2100,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Skill Attack"); #endif - /*Kayen: + /* Weapon Damage = spells[spell_id].base[i] Chance to Hit Bonus = spells[spell_id].base2[i] ???? = spells[spell_id].max[i] - MOST of the effects have this value. @@ -2812,11 +2812,10 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, return(effect_value); } -// solar: generic formula calculations +// generic formula calculations int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining) { /* -neotokyo: i need those formulas checked!!!! 0 = base 1 - 99 = base + level * formulaID @@ -2845,8 +2844,11 @@ neotokyo: i need those formulas checked!!!! int result = 0, updownsign = 1, ubase = base; if(ubase < 0) ubase = 0 - ubase; + int level_diff = caster_level - GetMinLevel(spell_id); + if (level_diff < 0) + level_diff = 0; - // solar: this updown thing might look messed up but if you look at the + // this updown thing might look messed up but if you look at the // spells it actually looks like some have a positive base and max where // the max is actually less than the base, hence they grow downward /* @@ -2875,17 +2877,17 @@ snare has both of them negative, yet their range should work the same: case 70: result = ubase/100; break; case 0: - case 100: // solar: confirmed 2/6/04 + case 100: // confirmed 2/6/04 result = ubase; break; - case 101: // solar: confirmed 2/6/04 + case 101: // confirmed 2/6/04 result = updownsign * (ubase + (caster_level / 2)); break; - case 102: // solar: confirmed 2/6/04 + case 102: // confirmed 2/6/04 result = updownsign * (ubase + caster_level); break; - case 103: // solar: confirmed 2/6/04 + case 103: // confirmed 2/6/04 result = updownsign * (ubase + (caster_level * 2)); break; - case 104: // solar: confirmed 2/6/04 + case 104: // confirmed 2/6/04 result = updownsign * (ubase + (caster_level * 3)); break; - case 105: // solar: confirmed 2/6/04 + case 105: // confirmed 2/6/04 result = updownsign * (ubase + (caster_level * 4)); break; case 107: @@ -2893,35 +2895,35 @@ snare has both of them negative, yet their range should work the same: result = updownsign * (ubase + (caster_level / 2)); break; case 108: result = updownsign * (ubase + (caster_level / 3)); break; - case 109: // solar: confirmed 2/6/04 + case 109: // confirmed 2/6/04 result = updownsign * (ubase + (caster_level / 4)); break; - case 110: // solar: confirmed 2/6/04 + case 110: // confirmed 2/6/04 //is there a reason we dont use updownsign here??? result = ubase + (caster_level / 5); break; case 111: - result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 6 * level_diff); break; case 112: - result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 8 * level_diff); break; case 113: - result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 10 * level_diff); break; case 114: - result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 15 * level_diff); break; //these formula were updated according to lucy 10/16/04 - case 115: // solar: this is only in symbol of transal - result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break; - case 116: // solar: this is only in symbol of ryltan - result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break; - case 117: // solar: this is only in symbol of pinzarn - result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break; - case 118: // solar: used in naltron and a few others - result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break; + case 115: // this is only in symbol of transal + result = ubase + 6 * level_diff; break; + case 116: // this is only in symbol of ryltan + result = ubase + 8 * level_diff; break; + case 117: // this is only in symbol of pinzarn + result = ubase + 12 * level_diff; break; + case 118: // used in naltron and a few others + result = ubase + 20 * level_diff; break; - case 119: // solar: confirmed 2/6/04 + case 119: // confirmed 2/6/04 result = ubase + (caster_level / 8); break; - case 121: // solar: corrected 2/6/04 + case 121: // corrected 2/6/04 result = ubase + (caster_level / 3); break; case 122: { @@ -2933,7 +2935,7 @@ snare has both of them negative, yet their range should work the same: result = updownsign * (ubase - (12 * ticdif)); break; } - case 123: // solar: added 2/6/04 + case 123: // added 2/6/04 result = MakeRandomInt(ubase, abs(max)); break; @@ -3304,7 +3306,7 @@ void Mob::DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, M } } -// solar: removes the buff in the buff slot 'slot' +// removes the buff in the buff slot 'slot' void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) { if(slot < 0 || slot > GetMaxTotalSlots()) @@ -5317,7 +5319,7 @@ int32 Mob::GetAdditionalDamage(Mob *caster, uint32 spell_id, bool use_skill, uin int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard) { - //Kayen 9-17-12: This is likely causing crashes, disabled till can resolve. + //9-17-12: This is likely causing crashes, disabled till can resolve. if (IsBard) return value; From 969f0c535e9a6e3ca20b25c036089aee347d2b77 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 22:44:29 -0700 Subject: [PATCH 02/15] Turn NameApprovalPacket handling into a method. --- world/client.cpp | 84 ++++++++++++++++++++++++++---------------------- world/client.h | 2 ++ 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 055126d10..6028fdd65 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -368,6 +368,50 @@ void Client::SendPostEnterWorld() { safe_delete(outapp); } +bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { + + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); + return false; + } + + snprintf(char_name, 64, "%s", (char*)app->pBuffer); + uchar race = app->pBuffer[64]; + uchar clas = app->pBuffer[68]; + + clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket; + outapp->SetOpcode(OP_ApproveName); + outapp->pBuffer = new uchar[1]; + outapp->size = 1; + + bool valid; + if(!database.CheckNameFilter(char_name)) { + valid = false; + } + else if(char_name[0] < 'A' && char_name[0] > 'Z') { + //name must begin with an upper-case letter. + valid = false; + } + else if (database.ReserveName(GetAccountID(), char_name)) { + valid = true; + } + else { + valid = false; + } + outapp->pBuffer[0] = valid? 1 : 0; + QueuePacket(outapp); + safe_delete(outapp); + + if(!valid) { + memset(char_name, 0, sizeof(char_name)); + } + + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -514,45 +558,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_ApproveName: //Name approval { - if (GetAccountID() == 0) { - clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); - ret = false; - break; - } - snprintf(char_name, 64, "%s", (char*)app->pBuffer); - uchar race = app->pBuffer[64]; - uchar clas = app->pBuffer[68]; - - clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket; - outapp->SetOpcode(OP_ApproveName); - outapp->pBuffer = new uchar[1]; - outapp->size = 1; - bool valid; - if(!database.CheckNameFilter(char_name)) { - valid = false; - } - else if(char_name[0] < 'A' && char_name[0] > 'Z') { - //name must begin with an upper-case letter. - valid = false; - } - else if (database.ReserveName(GetAccountID(), char_name)) { - valid = true; - } - else { - valid = false; - } - outapp->pBuffer[0] = valid? 1 : 0; - QueuePacket(outapp); - safe_delete(outapp); - - if(!valid) { - memset(char_name, 0, sizeof(char_name)); - } - - break; + return HandleNameApprovalPacket(app); } case OP_RandomNameGenerator: { diff --git a/world/client.h b/world/client.h index ea8f1a27c..755fe47c9 100644 --- a/world/client.h +++ b/world/client.h @@ -98,6 +98,8 @@ private: bool seencharsel; bool realfirstlogin; bool HandlePacket(const EQApplicationPacket *app); + bool HandleNameApprovalPacket(const EQApplicationPacket *app); + EQStreamInterface* const eqs; }; From 211248b50ed37110b356fda59c488e1a624155a0 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 22:47:19 -0700 Subject: [PATCH 03/15] Turn SendLoginInfoPacket handling into a method. --- world/client.cpp | 216 ++++++++++++++++++++++++----------------------- world/client.h | 1 + 2 files changed, 111 insertions(+), 106 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 6028fdd65..16ef41456 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -368,6 +368,114 @@ void Client::SendPostEnterWorld() { safe_delete(outapp); } +bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { + if (app->size != sizeof(LoginInfo_Struct)) { + return false; + } + + LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; + + // Quagmire - max len for name is 18, pass 15 + char name[19] = {0}; + char password[16] = {0}; + strn0cpy(name, (char*)li->login_info,18); + strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); + + if (strlen(password) <= 1) { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Login without a password"); + return false; + } + + pZoning=(li->zoning==1); + +#ifdef IPBASED_AUTH_HACK + struct in_addr tmpip; + tmpip.s_addr = ip; +#endif + uint32 id=0; + bool minilogin = loginserverlist.MiniLogin(); + if(minilogin){ + struct in_addr miniip; + miniip.s_addr = ip; + id = database.GetMiniLoginAccount(inet_ntoa(miniip)); + } + else if(strncasecmp(name, "LS#", 3) == 0) + id=atoi(&name[3]); + else + id=atoi(name); +#ifdef IPBASED_AUTH_HACK + if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password))) +#else + if (loginserverlist.Connected() == false && !pZoning) { + clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server."); + return false; + } + if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password))) +#endif + { + if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { + clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?"); + if(!minilogin) + clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable..."); + else + clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); + return false; + } + + cle->SetOnline(); + + clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)"); + + if(minilogin){ + WorldConfig::DisableStats(); + clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID()); + } + else { + clog(WORLD__CLIENT,"LS Account #%d",cle->LSID()); + } + + const WorldConfig *Config=WorldConfig::get(); + + if(Config->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSPlayerJoinWorld; + pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer; + strcpy(join->key,GetLSKey()); + join->lsaccount_id = GetLSID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } + + if (!pZoning) + SendGuildList(); + SendLogServer(); + SendApproveWorld(); + SendEnterWorld(cle->name()); + SendPostEnterWorld(); + if (!pZoning) { + SendExpansionInfo(); + SendCharInfo(); + database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); + } + + } + else { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name); + return false; + } + + if (!cle) + return true; + + cle->SetIP(GetIP()); + return true; +} + bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { @@ -412,6 +520,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { return true; } + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -449,112 +558,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { break; case OP_SendLoginInfo: { - if (app->size != sizeof(LoginInfo_Struct)) { - ret = false; - break; - } - - LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; - - // Quagmire - max len for name is 18, pass 15 - char name[19] = {0}; - char password[16] = {0}; - strn0cpy(name, (char*)li->login_info,18); - strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); - - if (strlen(password) <= 1) { - // TODO: Find out how to tell the client wrong username/password - clog(WORLD__CLIENT_ERR,"Login without a password"); - ret = false; - break; - } - - pZoning=(li->zoning==1); - -#ifdef IPBASED_AUTH_HACK - struct in_addr tmpip; - tmpip.s_addr = ip; -#endif - uint32 id=0; - bool minilogin = loginserverlist.MiniLogin(); - if(minilogin){ - struct in_addr miniip; - miniip.s_addr = ip; - id = database.GetMiniLoginAccount(inet_ntoa(miniip)); - } - else if(strncasecmp(name, "LS#", 3) == 0) - id=atoi(&name[3]); - else - id=atoi(name); -#ifdef IPBASED_AUTH_HACK - if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password))) -#else - if (loginserverlist.Connected() == false && !pZoning) { - clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server."); - ret = false; - break; - } - if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password))) -#endif - { - if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { - clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?"); - if(!minilogin) - clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable..."); - else - clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); - ret = false; - break; - } - - cle->SetOnline(); - - clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)"); - - if(minilogin){ - WorldConfig::DisableStats(); - clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID()); - } - else { - clog(WORLD__CLIENT,"LS Account #%d",cle->LSID()); - } - if(Config->UpdateStats){ - ServerPacket* pack = new ServerPacket; - pack->opcode = ServerOP_LSPlayerJoinWorld; - pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); - pack->pBuffer = new uchar[pack->size]; - memset(pack->pBuffer,0,pack->size); - ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer; - strcpy(join->key,GetLSKey()); - join->lsaccount_id = GetLSID(); - loginserverlist.SendPacket(pack); - safe_delete(pack); - } - - if (!pZoning) - SendGuildList(); - SendLogServer(); - SendApproveWorld(); - SendEnterWorld(cle->name()); - SendPostEnterWorld(); - if (!pZoning) { - SendExpansionInfo(); - SendCharInfo(); - database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); - } - - } - else { - // TODO: Find out how to tell the client wrong username/password - clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name); - ret = false; - break; - } - - if (!cle) - break; - cle->SetIP(GetIP()); - break; + return HandleSendLoginInfoPacket(app); } case OP_ApproveName: //Name approval { diff --git a/world/client.h b/world/client.h index 755fe47c9..848d9eba7 100644 --- a/world/client.h +++ b/world/client.h @@ -99,6 +99,7 @@ private: bool realfirstlogin; bool HandlePacket(const EQApplicationPacket *app); bool HandleNameApprovalPacket(const EQApplicationPacket *app); + bool HandleSendLoginInfoPacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From 18da8fe44d7e4ec659d1fed1c84b09d9774168eb Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 22:52:29 -0700 Subject: [PATCH 04/15] Turn GenerateRandomNamePacket handling into a method. --- world/client.cpp | 166 ++++++++++++++++++++++++----------------------- world/client.h | 2 + 2 files changed, 86 insertions(+), 82 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 16ef41456..b5335793e 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -520,6 +520,89 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { return true; } +bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { + // creates up to a 10 char name + char vowels[18]="aeiouyaeiouaeioe"; + char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; + char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; + int rndnum=MakeRandomInt(0, 75),n=1; + bool dlc=false; + bool vwl=false; + bool dbl=false; + if (rndnum>63) + { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel + rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" + rndname[0]=paircons[rndnum]; + rndname[1]=paircons[rndnum+1]; + n=2; + } + else if (rndnum>16) + { + rndnum-=17; + rndname[0]=cons[rndnum]; + } + else + { + rndname[0]=vowels[rndnum]; + vwl=true; + } + int namlen=MakeRandomInt(5, 10); + for (int i=n;i46) + { // pick a cons pair + if (i>namlen-3) // last 2 chars in name? + { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" + rndnum=MakeRandomInt(0, 7)*2; + } + else + { // pick any from the set + rndnum=(rndnum-47)*2; + } + rndname[i]=paircons[rndnum]; + rndname[i+1]=paircons[rndnum+1]; + dlc=true; // flag keeps second letter from being doubled below + i+=1; + } + else + { // select a single cons + rndname[i]=cons[rndnum]; + } + } + else + { // select a vowel + rndname[i]=vowels[MakeRandomInt(0, 16)]; + } + vwl=!vwl; + if (!dbl && !dlc) + { // one chance at double letters in name + if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name + { + rndname[i+1]=rndname[i]; + dbl=true; + i+=1; + } + } + } + + rndname[0]=toupper(rndname[0]); + NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; + memset(ngs->name,0,64); + strcpy(ngs->name,rndname); + +// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" }; +// //Could have parts of the random name in this struct and they compile together +// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; +// strncpy(ngs->name,"Notcreated",64); + + QueuePacket(app); + return true; +} bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); @@ -566,88 +649,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_RandomNameGenerator: { - // creates up to a 10 char name - char vowels[18]="aeiouyaeiouaeioe"; - char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; - char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; - int rndnum=MakeRandomInt(0, 75),n=1; - bool dlc=false; - bool vwl=false; - bool dbl=false; - if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" - rndname[0]=paircons[rndnum]; - rndname[1]=paircons[rndnum+1]; - n=2; - } - else if (rndnum>16) - { - rndnum-=17; - rndname[0]=cons[rndnum]; - } - else - { - rndname[0]=vowels[rndnum]; - vwl=true; - } - int namlen=MakeRandomInt(5, 10); - for (int i=n;i46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" - rndnum=MakeRandomInt(0, 7)*2; - } - else - { // pick any from the set - rndnum=(rndnum-47)*2; - } - rndname[i]=paircons[rndnum]; - rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below - i+=1; - } - else - { // select a single cons - rndname[i]=cons[rndnum]; - } - } - else - { // select a vowel - rndname[i]=vowels[MakeRandomInt(0, 16)]; - } - vwl=!vwl; - if (!dbl && !dlc) - { // one chance at double letters in name - if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name - { - rndname[i+1]=rndname[i]; - dbl=true; - i+=1; - } - } - } - - rndname[0]=toupper(rndname[0]); - NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; - memset(ngs->name,0,64); - strcpy(ngs->name,rndname); - -// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" }; -// //Could have parts of the random name in this struct and they compile together -// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; -// strncpy(ngs->name,"Notcreated",64); - - QueuePacket(app); - break; - + return HandleGenerateRandomNamePacket(app); } case OP_CharacterCreateRequest: { // New OpCode in SoF diff --git a/world/client.h b/world/client.h index 848d9eba7..e140c054e 100644 --- a/world/client.h +++ b/world/client.h @@ -97,9 +97,11 @@ private: bool firstlogin; bool seencharsel; bool realfirstlogin; + bool HandlePacket(const EQApplicationPacket *app); bool HandleNameApprovalPacket(const EQApplicationPacket *app); bool HandleSendLoginInfoPacket(const EQApplicationPacket *app); + bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From 5b3ec4fb7ce20abf35a712e9188cce4f6b2994eb Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 22:56:55 -0700 Subject: [PATCH 05/15] Turn CharacterCreateRequestPacket handling into a method. --- world/client.cpp | 100 +++++++++++++++++++++++------------------------ world/client.h | 1 + 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index b5335793e..aeb0e6615 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -595,15 +595,57 @@ bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { memset(ngs->name,0,64); strcpy(ngs->name,rndname); -// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" }; -// //Could have parts of the random name in this struct and they compile together -// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; -// strncpy(ngs->name,"Notcreated",64); - QueuePacket(app); return true; } +bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) { + // New OpCode in SoF + uint32 allocs = character_create_allocations.size(); + uint32 combos = character_create_race_class_combos.size(); + uint32 len = sizeof(RaceClassAllocation) * allocs; + len += sizeof(RaceClassCombos) * combos; + len += sizeof(uint8); + len += sizeof(uint32); + len += sizeof(uint32); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len); + unsigned char *ptr = outapp->pBuffer; + *((uint8*)ptr) = 0; + ptr += sizeof(uint8); + + *((uint32*)ptr) = allocs; + ptr += sizeof(uint32); + + for(int i = 0; i < allocs; ++i) { + RaceClassAllocation *alc = (RaceClassAllocation*)ptr; + + alc->Index = character_create_allocations[i].Index; + for(int j = 0; j < 7; ++j) { + alc->BaseStats[j] = character_create_allocations[i].BaseStats[j]; + alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j]; + } + ptr += sizeof(RaceClassAllocation); + } + + *((uint32*)ptr) = combos; + ptr += sizeof(uint32); + for(int i = 0; i < combos; ++i) { + RaceClassCombos *cmb = (RaceClassCombos*)ptr; + cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired; + cmb->Race = character_create_race_class_combos[i].Race; + cmb->Class = character_create_race_class_combos[i].Class; + cmb->Deity = character_create_race_class_combos[i].Deity; + cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex; + cmb->Zone = character_create_race_class_combos[i].Zone; + ptr += sizeof(RaceClassCombos); + } + + QueuePacket(outapp); + safe_delete(outapp); + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -651,53 +693,11 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { { return HandleGenerateRandomNamePacket(app); } - case OP_CharacterCreateRequest: { + case OP_CharacterCreateRequest: + { // New OpCode in SoF - uint32 allocs = character_create_allocations.size(); - uint32 combos = character_create_race_class_combos.size(); - uint32 len = sizeof(RaceClassAllocation) * allocs; - len += sizeof(RaceClassCombos) * combos; - len += sizeof(uint8); - len += sizeof(uint32); - len += sizeof(uint32); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len); - unsigned char *ptr = outapp->pBuffer; - *((uint8*)ptr) = 0; - ptr += sizeof(uint8); - - *((uint32*)ptr) = allocs; - ptr += sizeof(uint32); - - for(int i = 0; i < allocs; ++i) { - RaceClassAllocation *alc = (RaceClassAllocation*)ptr; - - alc->Index = character_create_allocations[i].Index; - for(int j = 0; j < 7; ++j) { - alc->BaseStats[j] = character_create_allocations[i].BaseStats[j]; - alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j]; - } - ptr += sizeof(RaceClassAllocation); - } - - *((uint32*)ptr) = combos; - ptr += sizeof(uint32); - for(int i = 0; i < combos; ++i) { - RaceClassCombos *cmb = (RaceClassCombos*)ptr; - cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired; - cmb->Race = character_create_race_class_combos[i].Race; - cmb->Class = character_create_race_class_combos[i].Class; - cmb->Deity = character_create_race_class_combos[i].Deity; - cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex; - cmb->Zone = character_create_race_class_combos[i].Zone; - ptr += sizeof(RaceClassCombos); - } - - QueuePacket(outapp); - safe_delete(outapp); - break; + return HandleCharacterCreateRequestPacket(app); } - case OP_CharacterCreate: //Char create { if (GetAccountID() == 0) diff --git a/world/client.h b/world/client.h index e140c054e..5b1dea0d1 100644 --- a/world/client.h +++ b/world/client.h @@ -102,6 +102,7 @@ private: bool HandleNameApprovalPacket(const EQApplicationPacket *app); bool HandleSendLoginInfoPacket(const EQApplicationPacket *app); bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app); + bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From cc07d511a51a47d5e9894c177d1e092aa1a43473 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 23:00:44 -0700 Subject: [PATCH 06/15] Turn CharacterCreatePacket handling into a method. --- world/client.cpp | 56 +++++++++++++++++++++++++++--------------------- world/client.h | 1 + 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index aeb0e6615..8f65c63cd 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -646,6 +646,36 @@ bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) return true; } +bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { + if (GetAccountID() == 0) + { + clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); + return false; + } + else if (app->size != sizeof(CharCreate_Struct)) + { + clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); + DumpPacket(app); + // the previous behavior was essentially returning true here + // but that seems a bit odd to me. + return true; + } + + CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; + if(OPCharCreate(char_name, cc) == false) + { + database.DeleteCharacter(char_name); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); + outapp->pBuffer[0] = 0; + QueuePacket(outapp); + safe_delete(outapp); + } + else + SendCharInfo(); + + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -700,31 +730,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_CharacterCreate: //Char create { - if (GetAccountID() == 0) - { - clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); - ret = false; - break; - } - else if (app->size != sizeof(CharCreate_Struct)) - { - clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); - DumpPacket(app); - break; - } - - CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; - if(OPCharCreate(char_name, cc) == false) - { - database.DeleteCharacter(char_name); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); - outapp->pBuffer[0] = 0; - QueuePacket(outapp); - safe_delete(outapp); - } - else - SendCharInfo(); - break; + return HandleCharacterCreatePacket(app); } case OP_EnterWorld: // Enter world { diff --git a/world/client.h b/world/client.h index 5b1dea0d1..3f1f9e30b 100644 --- a/world/client.h +++ b/world/client.h @@ -103,6 +103,7 @@ private: bool HandleSendLoginInfoPacket(const EQApplicationPacket *app); bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app); bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app); + bool HandleCharacterCreatePacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From 27493c3d751f5a52012fc696aedcb69282e65951 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 23:10:57 -0700 Subject: [PATCH 07/15] Turn EnterWorldPacket handling into a method. --- world/client.cpp | 472 ++++++++++++++++++++++++----------------------- world/client.h | 1 + 2 files changed, 246 insertions(+), 227 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 8f65c63cd..7039b8145 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -676,6 +676,223 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { return true; } +bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { + + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); + eqs->Close(); + return true; + } + + if(GetAdmin() < 0) + { + clog(WORLD__CLIENT,"Account banned or suspended."); + eqs->Close(); + return true; + } + + if (RuleI(World, MaxClientsPerIP) >= 0) { + client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection + } + + EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; + strn0cpy(char_name, ew->name, 64); + + EQApplicationPacket *outapp; + uint32 tmpaccid = 0; + charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID); + if (charid == 0 || tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name); + eqs->Close(); + return true; + } + + // Make sure this account owns this character + if (tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name); + eqs->Close(); + return true; + } + + if(!pZoning && ew->return_home) + { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool home_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->gohome[x] == 1) + { + home_enabled = true; + return true; + } + } + } + safe_delete(cs); + + if(home_enabled) + { + zoneID = database.MoveCharacterToBind(charid,4); + } + else + { + clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name); + database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); + eqs->Close(); + return true; + } + } + + if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool tutorial_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->tutorial[x] == 1) + { + tutorial_enabled = true; + return true; + } + } + } + safe_delete(cs); + + if(tutorial_enabled) + { + zoneID = RuleI(World, TutorialZoneID); + database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); + } + else + { + clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name); + database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); + eqs->Close(); + return true; + } + } + + if (zoneID == 0 || !database.GetZoneName(zoneID)) { + // This is to save people in an invalid zone, once it's removed from the DB + database.MoveCharacterToZone(charid, "arena"); + clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name); + } + + if(instanceID > 0) + { + if(!database.VerifyInstanceAlive(instanceID, GetCharID())) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + else + { + if(!database.VerifyZoneInstance(zoneID, instanceID)) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + } + } + + if(!pZoning) { + database.SetGroupID(char_name, 0, charid); + database.SetLoginFlags(charid, false, false, 1); + } + else{ + uint32 groupid=database.GetGroupID(char_name); + if(groupid>0){ + char* leader=0; + char leaderbuf[64]={0}; + if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){ + EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; + gj->action=8; + strcpy(gj->yourname,char_name); + strcpy(gj->membername,leader); + QueuePacket(outapp3); + safe_delete(outapp3); + } + } + } + + outapp = new EQApplicationPacket(OP_MOTD); + char tmp[500] = {0}; + if (database.GetVariable("MOTD", tmp, 500)) { + outapp->size = strlen(tmp)+1; + outapp->pBuffer = new uchar[outapp->size]; + memset(outapp->pBuffer,0,outapp->size); + strcpy((char*)outapp->pBuffer, tmp); + + } else { + // Null Message of the Day. :) + outapp->size = 1; + outapp->pBuffer = new uchar[outapp->size]; + outapp->pBuffer[0] = 0; + } + QueuePacket(outapp); + safe_delete(outapp); + + int MailKey = MakeRandomInt(1, INT_MAX); + + database.SetMailKey(charid, GetIP(), MailKey); + + char ConnectionType; + + if(ClientVersionBit & BIT_UnderfootAndLater) + ConnectionType = 'U'; + else if(ClientVersionBit & BIT_SoFAndLater) + ConnectionType = 'S'; + else + ConnectionType = 'C'; + + EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer); + char buffer[112]; + + const WorldConfig *Config = WorldConfig::get(); + + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + outapp2 = new EQApplicationPacket(OP_SetChatServer2); + + if(ClientVersionBit & BIT_TitaniumAndEarlier) + ConnectionType = 'M'; + + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + EnterWorld(); + + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -710,7 +927,9 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { switch(opcode) { case OP_CrashDump: - break; + { + return true; + } case OP_SendLoginInfo: { return HandleSendLoginInfoPacket(app); @@ -734,217 +953,10 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_EnterWorld: // Enter world { - if (GetAccountID() == 0) { - clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); - eqs->Close(); - break; - } - if(GetAdmin() < 0) - { - clog(WORLD__CLIENT,"Account banned or suspended."); - eqs->Close(); - break; - } - - if (RuleI(World, MaxClientsPerIP) >= 0) { - client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection - } - - EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; - strn0cpy(char_name, ew->name, 64); - - EQApplicationPacket *outapp; - uint32 tmpaccid = 0; - charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID); - if (charid == 0 || tmpaccid != GetAccountID()) { - clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name); - eqs->Close(); - break; - } - - // Make sure this account owns this character - if (tmpaccid != GetAccountID()) { - clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name); - eqs->Close(); - break; - } - - if(!pZoning && ew->return_home) - { - CharacterSelect_Struct* cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); - bool home_enabled = false; - - for(int x = 0; x < 10; ++x) - { - if(strcasecmp(cs->name[x], char_name) == 0) - { - if(cs->gohome[x] == 1) - { - home_enabled = true; - break; - } - } - } - safe_delete(cs); - - if(home_enabled) - { - zoneID = database.MoveCharacterToBind(charid,4); - } - else - { - clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name); - database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); - eqs->Close(); - break; - } - } - - if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { - CharacterSelect_Struct* cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); - bool tutorial_enabled = false; - - for(int x = 0; x < 10; ++x) - { - if(strcasecmp(cs->name[x], char_name) == 0) - { - if(cs->tutorial[x] == 1) - { - tutorial_enabled = true; - break; - } - } - } - safe_delete(cs); - - if(tutorial_enabled) - { - zoneID = RuleI(World, TutorialZoneID); - database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); - } - else - { - clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name); - database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); - eqs->Close(); - break; - } - } - - if (zoneID == 0 || !database.GetZoneName(zoneID)) { - // This is to save people in an invalid zone, once it's removed from the DB - database.MoveCharacterToZone(charid, "arena"); - clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name); - } - - if(instanceID > 0) - { - if(!database.VerifyInstanceAlive(instanceID, GetCharID())) - { - zoneID = database.MoveCharacterToBind(charid); - instanceID = 0; - } - else - { - if(!database.VerifyZoneInstance(zoneID, instanceID)) - { - zoneID = database.MoveCharacterToBind(charid); - instanceID = 0; - } - } - } - - if(!pZoning) { - database.SetGroupID(char_name, 0, charid); - database.SetLoginFlags(charid, false, false, 1); - } - else{ - uint32 groupid=database.GetGroupID(char_name); - if(groupid>0){ - char* leader=0; - char leaderbuf[64]={0}; - if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){ - EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; - gj->action=8; - strcpy(gj->yourname,char_name); - strcpy(gj->membername,leader); - QueuePacket(outapp3); - safe_delete(outapp3); - } - } - } - - outapp = new EQApplicationPacket(OP_MOTD); - char tmp[500] = {0}; - if (database.GetVariable("MOTD", tmp, 500)) { - outapp->size = strlen(tmp)+1; - outapp->pBuffer = new uchar[outapp->size]; - memset(outapp->pBuffer,0,outapp->size); - strcpy((char*)outapp->pBuffer, tmp); - - } else { - // Null Message of the Day. :) - outapp->size = 1; - outapp->pBuffer = new uchar[outapp->size]; - outapp->pBuffer[0] = 0; - } - QueuePacket(outapp); - safe_delete(outapp); - - int MailKey = MakeRandomInt(1, INT_MAX); - - database.SetMailKey(charid, GetIP(), MailKey); - - char ConnectionType; - - if(ClientVersionBit & BIT_UnderfootAndLater) - ConnectionType = 'U'; - else if(ClientVersionBit & BIT_SoFAndLater) - ConnectionType = 'S'; - else - ConnectionType = 'C'; - - EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer); - char buffer[112]; - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); - - outapp2 = new EQApplicationPacket(OP_SetChatServer2); - - if(ClientVersionBit & BIT_TitaniumAndEarlier) - ConnectionType = 'M'; - - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); - - EnterWorld(); - break; + return HandleEnterWorldPacket(app); } case OP_LoginComplete:{ - break; + return true; } case OP_DeleteCharacter: { uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); @@ -958,47 +970,53 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_ApproveWorld: { - break; + return true; } - case OP_WorldClientReady:{ - break; + case OP_WorldClientReady: + { + return true; } case OP_World_Client_CRC1: - case OP_World_Client_CRC2: { + case OP_World_Client_CRC2: + { // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, // then 'Start Tutorial' was not chosen. StartInTutorial = false; - break; + return true; } - case OP_WearChange: { // User has selected a different character - break; + case OP_WearChange: + { // User has selected a different character + return true; } - case OP_WorldComplete: { + case OP_WorldComplete: + { eqs->Close(); - break; + return true; } case OP_LoginUnknown1: case OP_LoginUnknown2: - break; - + { + return true; + } case OP_ZoneChange: + { // HoT sends this to world while zoning and wants it echoed back. if(ClientVersionBit & BIT_RoFAndLater) { QueuePacket(app); } - break; - - - default: { + return true; + } + default: + { clog(WORLD__CLIENT_ERR,"Received unknown EQApplicationPacket"); _pkt(WORLD__CLIENT_ERR,app); - break; + return true; } } - return ret; + return true; } bool Client::Process() { diff --git a/world/client.h b/world/client.h index 3f1f9e30b..207f48e25 100644 --- a/world/client.h +++ b/world/client.h @@ -104,6 +104,7 @@ private: bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app); bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app); bool HandleCharacterCreatePacket(const EQApplicationPacket *app); + bool HandleEnterWorldPacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From ea606ef80dc958a09234d6131462cf21b6e200f6 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 23:14:00 -0700 Subject: [PATCH 08/15] Turn DeleteCharacterPacket handling into a method. --- world/client.cpp | 28 ++++++++++++++++++---------- world/client.h | 1 + 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 7039b8145..b275640e0 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -893,6 +893,19 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { return true; } +bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { + + uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); + if(char_acct_id == GetAccountID()) + { + clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); + database.DeleteCharacter((char *)app->pBuffer); + SendCharInfo(); + } + + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { const WorldConfig *Config=WorldConfig::get(); EmuOpcode opcode = app->GetOpcode(); @@ -955,18 +968,13 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { { return HandleEnterWorldPacket(app); } - case OP_LoginComplete:{ + case OP_LoginComplete: + { return true; } - case OP_DeleteCharacter: { - uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); - if(char_acct_id == GetAccountID()) - { - clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); - database.DeleteCharacter((char *)app->pBuffer); - SendCharInfo(); - } - break; + case OP_DeleteCharacter: + { + return HandleDeleteCharacterPacket(app); } case OP_ApproveWorld: { diff --git a/world/client.h b/world/client.h index 207f48e25..bdef569f1 100644 --- a/world/client.h +++ b/world/client.h @@ -105,6 +105,7 @@ private: bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app); bool HandleCharacterCreatePacket(const EQApplicationPacket *app); bool HandleEnterWorldPacket(const EQApplicationPacket *app); + bool HandleDeleteCharacterPacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From 495510a02ec0a66240257175ff68d8135388cf7d Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 23:18:40 -0700 Subject: [PATCH 09/15] Reorganized handling of Packets to be a bit cleaner. --- world/client.cpp | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index b275640e0..a245f88a7 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -907,14 +907,12 @@ bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { } bool Client::HandlePacket(const EQApplicationPacket *app) { - const WorldConfig *Config=WorldConfig::get(); + EmuOpcode opcode = app->GetOpcode(); clog(WORLD__CLIENT_TRACE,"Recevied EQApplicationPacket"); _pkt(WORLD__CLIENT_TRACE,app); - bool ret = true; - if (!eqs->CheckState(ESTABLISHED)) { clog(WORLD__CLIENT,"Client disconnected (net inactive on send)"); return false; @@ -939,8 +937,14 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { switch(opcode) { - case OP_CrashDump: + case OP_World_Client_CRC1: + case OP_World_Client_CRC2: { + // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button + // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent + // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, + // then 'Start Tutorial' was not chosen. + StartInTutorial = false; return true; } case OP_SendLoginInfo: @@ -968,36 +972,10 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { { return HandleEnterWorldPacket(app); } - case OP_LoginComplete: - { - return true; - } case OP_DeleteCharacter: { return HandleDeleteCharacterPacket(app); } - case OP_ApproveWorld: - { - return true; - } - case OP_WorldClientReady: - { - return true; - } - case OP_World_Client_CRC1: - case OP_World_Client_CRC2: - { - // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button - // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent - // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, - // then 'Start Tutorial' was not chosen. - StartInTutorial = false; - return true; - } - case OP_WearChange: - { // User has selected a different character - return true; - } case OP_WorldComplete: { eqs->Close(); @@ -1005,6 +983,11 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { } case OP_LoginUnknown1: case OP_LoginUnknown2: + case OP_CrashDump: + case OP_WearChange: + case OP_LoginComplete: + case OP_ApproveWorld: + case OP_WorldClientReady: { return true; } From b09a3840eb226e5585ef6e685e8e6fa7aef9ee79 Mon Sep 17 00:00:00 2001 From: Arthur Dene Ice Date: Mon, 22 Apr 2013 23:22:13 -0700 Subject: [PATCH 10/15] Turn HandleZoneChangePacket into method. --- world/client.cpp | 27 +++++++++++++++++---------- world/client.h | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index a245f88a7..5f47d36eb 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -906,6 +906,15 @@ bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { return true; } +bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) { + // HoT sends this to world while zoning and wants it echoed back. + if(ClientVersionBit & BIT_RoFAndLater) + { + QueuePacket(app); + } + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { EmuOpcode opcode = app->GetOpcode(); @@ -981,6 +990,11 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { eqs->Close(); return true; } + case OP_ZoneChange: + { + // HoT sends this to world while zoning and wants it echoed back. + return HandleZoneChangePacket(app); + } case OP_LoginUnknown1: case OP_LoginUnknown2: case OP_CrashDump: @@ -988,16 +1002,9 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { case OP_LoginComplete: case OP_ApproveWorld: case OP_WorldClientReady: - { - return true; - } - case OP_ZoneChange: - { - // HoT sends this to world while zoning and wants it echoed back. - if(ClientVersionBit & BIT_RoFAndLater) - { - QueuePacket(app); - } + { + // Essentially we are just 'eating' these packets, indicating + // they are handled. return true; } default: diff --git a/world/client.h b/world/client.h index bdef569f1..877ec17a0 100644 --- a/world/client.h +++ b/world/client.h @@ -106,6 +106,7 @@ private: bool HandleCharacterCreatePacket(const EQApplicationPacket *app); bool HandleEnterWorldPacket(const EQApplicationPacket *app); bool HandleDeleteCharacterPacket(const EQApplicationPacket *app); + bool HandleZoneChangePacket(const EQApplicationPacket *app); EQStreamInterface* const eqs; }; From b15cb08f549172f4f996be65af911888a5ce5c95 Mon Sep 17 00:00:00 2001 From: Derision Date: Wed, 24 Apr 2013 19:31:13 +0100 Subject: [PATCH 11/15] EQExtractor2 update. --- utils/EQExtractor2/EQExtractor2/ChangeLog.txt | 3 + .../EQExtractor2/EQApplicationLayer.cs | 2 + .../EQExtractor2/EQExtractor2.csproj | 4 + .../EQExtractor2/EQExtractor2Form1.cs | 2 +- .../EQExtractor2/PatchApril15-2013.cs | 23 + .../EQExtractor2/patch_April15-2013.conf | 653 ++++++++++++++++++ 6 files changed, 686 insertions(+), 1 deletion(-) create mode 100644 utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs create mode 100644 utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf diff --git a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt index be8d241dc..abdfab715 100644 --- a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt +++ b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt @@ -1,5 +1,8 @@ EQExtractor2 Changelog. All changes since the 1.0 release. +==04/24/2013== +Derision: Added decoder to support packet dumps from current Live client. SQL generation NOT SUPPORTED YET (need tweaks to position struct). + ==03/17/2013== Derision: Added SQL generation support to current Live client decoder. diff --git a/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs index b6732ca82..58537e9be 100644 --- a/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs +++ b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs @@ -104,6 +104,8 @@ namespace EQApplicationLayer PatchList.Add(new PatchFebruary112013Decoder()); PatchList.Add(new PatchMarch132013Decoder()); + + PatchList.Add(new PatchApril152013Decoder()); PatchList.Add(new PatchSoD()); diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj index 6e56a9c58..542700b6c 100644 --- a/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj @@ -72,6 +72,7 @@ LogForm.cs + @@ -137,6 +138,9 @@ UserOptions.cs + + Always + Always diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs index 7f28f792a..220c743d9 100644 --- a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs @@ -18,7 +18,7 @@ namespace EQExtractor2 { public partial class EQExtractor2Form1 : Form { - string Version = "EQExtractor2 Version 2.6.2 SVN"; + string Version = "EQExtractor2 Version 2.6.3 SVN"; static int PacketsSeen = 0; static long BytesRead = 0; diff --git a/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs new file mode 100644 index 000000000..7d8804577 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchApril152013Decoder : PatchMarch132013Decoder + { + public PatchApril152013Decoder() + { + Version = "EQ Client Build Date April 15 2013."; + + PatchConfFileName = "patch_April15-2013.conf"; + + SupportsSQLGeneration = false; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf new file mode 100644 index 000000000..94d57d345 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_April15-2013.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x5f21 +OP_ApproveWorld=0x6604 +OP_LogServer=0x27a4 +OP_SendCharInfo=0x6b98 +OP_ExpansionInfo=0x0322 +OP_GuildsList=0x3d34 +OP_EnterWorld=0x70c9 +OP_PostEnterWorld=0x29f7 +OP_World_Client_CRC1=0x0786 +OP_World_Client_CRC2=0x77cd +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x5a84 +OP_SendMembership=0x3603 +OP_SendMembershipDetails=0x3222 +OP_CharacterCreateRequest=0x329a +OP_CharacterCreate=0x7f24 +OP_DeleteCharacter=0x3078 +OP_RandomNameGenerator=0x2617 +OP_ApproveName=0x657d +OP_MOTD=0x0db2 +OP_SetChatServer=0x6a13 +OP_SetChatServer2=0x48a1 +OP_ZoneServerInfo=0x4cae +OP_WorldComplete=0x1db8 +OP_WorldUnknown001=0x5810 +OP_FloatListThing=0x1ada + +# Reasons for Disconnect: +OP_ZoneUnavail=0x2a10 +OP_WorldClientReady=0x0b64 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x58f7 +OP_ZoneEntry=0x31d8 +OP_ReqNewZone=0x3cb7 +OP_NewZone=0x0bf6 +OP_ZoneSpawns=0x36f6 +OP_PlayerProfile=0x6b5b +OP_TimeOfDay=0x3377 +OP_LevelUpdate=0x3e3c +OP_Stamina=0x5813 +OP_RequestClientZoneChange=0x1b24 +OP_ZoneChange=0x5538 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x130f +OP_ChangeSize=0x3619 +OP_TributeUpdate=0x365d +OP_TributeTimer=0x7ea7 +OP_SendTributes=0x57f5 +OP_SendGuildTributes=0x430c +OP_TributeInfo=0x3c65 +OP_Weather=0x4ec0 +OP_ReqClientSpawn=0x16ca +OP_SpawnDoor=0x375d +OP_GroundSpawn=0x3a2b +OP_SendZonepoints=0x41b8 +OP_BlockedBuffs=0x52b5 +OP_RemoveBlockedBuffs=0x5884 +OP_ClearBlockedBuffs=0x565c +OP_WorldObjectsSent=0x37d3 +OP_SendExpZonein=0x7d57 +OP_SendAATable=0x65c4 +OP_RespondAA=0x0b69 +OP_UpdateAA=0x67e0 +OP_SendAAStats=0x702d +OP_AAExpUpdate=0x4616 +OP_ExpUpdate=0x39a0 +OP_HPUpdate=0x648b +OP_ManaChange=0x2ed8 +OP_TGB=0x6516 +OP_SpecialMesg=0x0000 +OP_GuildMemberList=0x2bad +OP_GuildMOTD=0x4e44 +OP_CharInventory=0x3deb +OP_WearChange=0x1ff6 +OP_ClientUpdate=0x6962 +OP_ClientReady=0x0c66 # 0x422d +OP_SetServerFilter=0x3b5b + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x6ce8 # Was 0x35dc +OP_GetGuildMOTDReply=0x150b # Was 0x4586 +OP_GuildMemberUpdate=0x665a # Was 0x5643 +OP_GuildInvite=0x3834 +OP_GuildRemove=0x1eb3 +OP_GuildPeace=0x5b6e +OP_SetGuildMOTD=0x183e +OP_GuildList=0x0000 +OP_GuildWar=0x7e3d +OP_GuildLeader=0x0c63 +OP_GuildDelete=0x1e22 +OP_GuildInviteAccept=0x3462 +OP_GuildDemote=0x47ed +OP_GuildPublicNote=0x6208 +OP_GuildManageBanker=0x2010 # Was 0x0737 +OP_GuildBank=0x3d39 # Was 0x10c3 +OP_SetGuildRank=0x671c +OP_GuildUpdateURLAndChannel=0x31fc +OP_GuildStatus=0x7b81 +OP_GuildCreate=0x52c7 # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x4946 +OP_GMBecomeNPC=0x2b92 +OP_GMZoneRequest=0x7a65 +OP_GMZoneRequest2=0x5585 +OP_GMGoto=0x15b4 +OP_GMSearchCorpse=0x594b +OP_GMHideMe=0x0005 +OP_GMDelCorpse=0x0633 +OP_GMApproval=0x4bbf +OP_GMToggle=0x5e6f +OP_GMSummon=0x7b50 # Was 0x684f +OP_GMEmoteZone=0x735a # Was 0x0655 +OP_GMEmoteWorld=0x2114 # Was 0x1935 +OP_GMFind=0x4e59 +OP_GMKick=0x5c02 +OP_GMKill=0x2728 +OP_GMNameChange=0x7b23 # Was 0x4434 +OP_GMLastName=0x1063 # Was 0x3077 + +# Misc Opcodes +OP_InspectRequest=0x5d21 +OP_InspectAnswer=0x5569 +OP_InspectMessageUpdate=0x09cd +OP_BeginCast=0x557a +OP_BuffFadeMsg=0x6226 +OP_ConsentResponse=0x0333 +OP_MemorizeSpell=0x7bc9 +OP_SwapSpell=0x3217 +OP_CastSpell=0x3f6d +OP_Consider=0x3815 +OP_FormattedMessage=0x52c0 +OP_SimpleMessage=0x0e29 +OP_Buff=0x0dd5 +OP_Illusion=0x1182 +OP_MoneyOnCorpse=0x05e8 +OP_RandomReply=0x4b1a +OP_DenyResponse=0x3918 +OP_SkillUpdate=0x4b94 +OP_GMTrainSkillConfirm=0x0498 # 0x3960 +OP_RandomReq=0x4629 +OP_Death=0x1aa0 +OP_GMTraining=0x698c +OP_GMEndTraining=0x36dc +OP_GMTrainSkill=0x580c +OP_Animation=0x172f +OP_Begging=0x39a6 +OP_Consent=0x2c4e +OP_ConsentDeny=0x69bb +OP_AutoFire=0x086e +OP_PetCommands=0x17bc +OP_DeleteSpell=0x709b +OP_Surname=0x0329 +OP_ClearSurname=0x6182 +OP_FaceChange=0x6b0e +OP_SenseHeading=0x12cf +OP_Action=0x7329 +OP_ConsiderCorpse=0x28ed +OP_HideCorpse=0x0179 +OP_CorpseDrag=0x47d9 +OP_CorpseDrop=0x0df7 +OP_Bug=0x51a4 +OP_Feedback=0x2061 +OP_Report=0x4c00 +OP_Damage=0x4725 +OP_ChannelMessage=0x0dc9 +OP_Assist=0x68d3 +OP_AssistGroup=0x6c10 +OP_MoveCoin=0x37b3 +OP_ZonePlayerToBind=0x36ad +OP_KeyRing=0x7ac2 +OP_WhoAllRequest=0x7cf3 +OP_WhoAllResponse=0x14cc +OP_FriendsWho=0x7ee9 +OP_ConfirmDelete=0x13c1 +OP_Logout=0x226f +OP_Rewind=0x7562 +OP_TargetCommand=0x3bd6 +OP_Hide=0x4517 +OP_Jump=0x1dbf +OP_Camp=0x6aa7 +OP_Emote=0x6195 +OP_SetRunMode=0x068d +OP_BankerChange=0x4d5b +OP_TargetMouse=0x3f43 +OP_MobHealth=0x52dc +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x2baa +OP_XTargetResponse=0x0be2 +OP_XTargetRequest=0x024c +OP_XTargetAutoAddHaters=0x3729 +OP_TargetBuffs=0x241c +OP_BuffCreate=0x6878 +OP_BuffRemoveRequest=0x78ab +OP_DeleteSpawn=0x5279 +OP_AutoAttack=0x1bf5 +OP_AutoAttack2=0x0ce9 +OP_Consume=0x7711 +OP_MoveItem=0x2bb9 +OP_DeleteItem=0x5c68 +OP_DeleteCharge=0x7660 +OP_ItemPacket=0x154e +OP_ItemLinkResponse=0x62f9 +OP_ItemLinkClick=0x53c8 +OP_ItemPreview=0x0ee7 +OP_NewSpawn=0x61e9 +OP_Track=0x7f7a +OP_TrackTarget=0x4190 +OP_TrackUnknown=0x3a5e +OP_ClickDoor=0x349b +OP_MoveDoor=0x4920 +OP_RemoveAllDoors=0x6542 +OP_EnvDamage=0x7912 +OP_BoardBoat=0x6a35 +OP_Forage=0x7621 +OP_LeaveBoat=0x7251 +OP_ControlBoat=0x5635 +OP_SafeFallSuccess=0x3358 +OP_RezzComplete=0x570d +OP_RezzRequest=0x3e8b +OP_RezzAnswer=0x77c7 +OP_Shielding=0x3f0b +OP_RequestDuel=0x0a38 +OP_MobRename=0x2638 +OP_AugmentItem=0x4695 # Was 0x37cb +OP_WeaponEquip1=0x1d1d +OP_WeaponEquip2=0x6147 # Was 0x6022 +OP_WeaponUnequip2=0x4b3e # Was 0x0110 +OP_ApplyPoison=0x1b5b +OP_Save=0x6444 +OP_TestBuff=0x2c3c # Was 0x3772 +OP_CustomTitles=0x61ab +OP_Split=0x3dff +OP_YellForHelp=0x694a +OP_LoadSpellSet=0x5d60 +OP_Bandolier=0x1d9d +OP_PotionBelt=0x5499 # Was 0x4d3b +OP_DuelResponse=0x3a06 +OP_DuelResponse2=0x7929 +OP_SaveOnZoneReq=0x0b2e +OP_ReadBook=0x1b02 +OP_Dye=0x0999 +OP_InterruptCast=0x57e1 +OP_AAAction=0x3994 +OP_LeadershipExpToggle=0x2746 +OP_LeadershipExpUpdate=0x0fdc +OP_PurchaseLeadershipAA=0x0f78 +OP_UpdateLeadershipAA=0x4ef0 +OP_MarkNPC=0x70e1 +OP_ClearNPCMarks=0x643a +OP_DelegateAbility=0x2aa5 +OP_SetGroupTarget=0x19d0 +OP_Charm=0x5221 +OP_Stun=0x5826 +OP_SendFindableNPCs=0x743b +OP_FindPersonRequest=0x3560 +OP_FindPersonReply=0x0844 +OP_Sound=0x1b73 +OP_PetBuffWindow=0x56c0 +OP_LevelAppearance=0x6698 +OP_Translocate=0x091a +OP_Sacrifice=0x5b22 +OP_PopupResponse=0x1475 +OP_OnLevelMessage=0x1cbd +OP_AugmentInfo=0x6524 +OP_Petition=0x3b76 +OP_SomeItemPacketMaybe=0x0668 +OP_PVPStats=0x3388 # Unsure +OP_PVPLeaderBoardRequest=0x2852 +OP_PVPLeaderBoardReply=0x65cf +OP_PVPLeaderBoardDetailsRequest=0x1d11 +OP_PVPLeaderBoardDetailsReply=0x1a8e +OP_RestState=0x3018 +OP_RespawnWindow=0x7b02 +OP_LDoNButton=0x4438 +OP_SetStartCity=0x2121 # Was 0x2d1b +OP_VoiceMacroIn=0x6f3d +OP_VoiceMacroOut=0x0577 +OP_ItemViewUnknown=0x31a8 +OP_VetRewardsAvaliable=0x0687 +OP_VetClaimRequest=0x0407 +OP_VetClaimReply=0x1ef4 +OP_DisciplineUpdate=0x06da # Was 0x2f05 +OP_DisciplineTimer=0x2a91 # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x6d4b +OP_NPCMoveUpdate=0x5a39 +OP_CameraEffect=0x5099 +OP_SpellEffect=0x1e99 +OP_RemoveNimbusEffect=0x29af +OP_AltCurrency=0x797d +OP_AltCurrencyMerchantRequest=0x5132 +OP_AltCurrencyMerchantReply=0x7ec5 +OP_AltCurrencyPurchase=0x6731 +OP_AltCurrencySell=0x5420 +OP_AltCurrencySellSelection=0x6fed +OP_AltCurrencyReclaim=0x5eda +OP_CrystalCountUpdate=0x3b26 # Was 0x3f60 +OP_CrystalCreate=0x39ed # Was 0x5a82 +OP_CrystalReclaim=0x369e # Was 0x7616 +OP_Untargetable=0x6c0b +OP_IncreaseStats=0x757b +OP_Weblink=0x4173 +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x0000 + +OP_DzQuit=0x7579 +OP_DzListTimers=0x00e5 +OP_DzAddPlayer=0x070f +OP_DzRemovePlayer=0x11eb +OP_DzSwapPlayer=0x72fe +OP_DzMakeLeader=0x3904 +OP_DzPlayerList=0x20a4 +OP_DzJoinExpeditionConfirm=0x1c57 +OP_DzJoinExpeditionReply=0x3274 +OP_DzExpeditionInfo=0xe86b +OP_DzExpeditionList=0x1349 +OP_DzMemberStatus=0x638e +OP_DzLeaderStatus=0x39cf +OP_DzExpeditionEndsWarning=0x44eb +OP_DzMemberList=0x0000 +OP_DzCompass=0x5baa # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x4c12 +OP_EnduranceUpdate=0x250e +OP_MobManaUpdate=0x230a +OP_MobEnduranceUpdate=0x7012 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x1d60 +OP_MercenaryDataUpdate=0x0c2d +OP_MercenaryDataRequest=0x8fa2 +OP_MercenaryDataResponse=0x6a0c +OP_MercenaryHire=0x05bd +OP_MercenaryDismiss=0x376f +OP_MercenaryTimerRequest=0x293d +OP_MercenaryTimer=0x3f92 +OP_MercenaryUnknown1=0x1c14 +OP_MercenaryCommand=0x26b9 +OP_MercenarySuspendRequest=0x4b19 +OP_MercenarySuspendResponse=0x0830 +OP_MercenaryUnsuspendResponse=0x4405 + +# Looting +OP_LootRequest=0x52ba +OP_EndLootRequest=0x0573 +OP_LootItem=0x2344 +OP_LootComplete=0x71c6 + +# bazaar trader stuff: +OP_BazaarSearch=0x6c36 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x081e +OP_TraderShop=0x327c +OP_Trader=0x2284 # Was 0x6790 +OP_TraderBuy=0x0000 +OP_Barter=0x241f +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x164e +OP_TradeAcceptClick=0x61eb +OP_TradeRequestAck=0x76c1 +OP_TradeCoins=0x582b +OP_FinishTrade=0x6bba +OP_CancelTrade=0x5a9f +OP_TradeMoneyUpdate=0x2432 +OP_MoneyUpdate=0x52e5 +OP_TradeBusy=0x6482 + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x5ea3 +OP_FinishWindow2=0x6177 + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x49fc +OP_ItemVerifyReply=0x0061 + +# merchant stuff +OP_ShopPlayerSell=0x1961 +OP_ShopRequest=0x393f +OP_ShopEnd=0x7385 +OP_ShopEndConfirm=0x2ed5 +OP_ShopPlayerBuy=0x0f9d +OP_ShopDelItem=0x78d2 + +# tradeskill stuff: +OP_ClickObject=0x6cb6 +OP_ClickObjectAction=0x5d27 +OP_ClearObject=0x3a64 +OP_RecipeDetails=0x2d9b +OP_RecipesFavorite=0x2875 +OP_RecipesSearch=0x5013 +OP_RecipeReply=0x4161 +OP_RecipeAutoCombine=0x74ac +OP_TradeSkillCombine=0x48fd + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x5ba2 +OP_OpenTributeMaster=0x6c0f # Was 0x40f5 +OP_SelectTribute=0x003e +OP_TributeItem=0x07cd +OP_TributeMoney=0x2f60 # Was 0x6fed +OP_TributeToggle=0x420c +OP_TributePointUpdate=0x5552 +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x234c +OP_AdventureFinish=0x33a0 +OP_AdventureInfoRequest=0x00af +OP_AdventureInfo=0x6a8c +OP_AdventureRequest=0x1c3b +OP_AdventureDetails=0x4b02 +OP_AdventureData=0x0e46 +OP_AdventureUpdate=0x724c +OP_AdventureMerchantRequest=0x6d4f # Was 654d +OP_AdventureMerchantResponse=0x7d2d # Was 7949 +OP_AdventureMerchantPurchase=0x3afc # Was 155a +OP_AdventureMerchantSell=0x19eb # Was 389c +OP_AdventurePointsUpdate=0x7df1 # Was 7589 +OP_AdventureStatsRequest=0x0dc1 +OP_AdventureStatsReply=0x7ca8 +OP_AdventureLeaderboardRequest=0x4769 +OP_AdventureLeaderboardReply=0x08fc + +# Group Opcodes +OP_GroupDisband=0x4e45 +OP_GroupInvite=0x1694 +OP_GroupFollow=0x6246 +OP_GroupUpdate=0x31c8 +OP_GroupUpdateB=0x13cd +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x4dbe +OP_GroupDelete=0x6d6b +OP_CancelInvite=0x6417 +OP_GroupFollow2=0x26fc +OP_GroupInvite2=0x6232 +OP_GroupDisbandYou=0x14ca +OP_GroupDisbandOther=0x0f0b +OP_GroupLeaderChange=0x1cdb +OP_GroupRoles=0x75de +OP_GroupMakeLeader=0x24b9 +OP_DoGroupLeadershipAbility=0x46df +OP_GroupLeadershipAAUpdate=0x3b89 + +# LFG/LFP Opcodes +OP_LFGCommand=0x4719 +OP_LFGGetMatchesRequest=0x78db +OP_LFGGetMatchesResponse=0x36b4 +OP_LFPGetMatchesRequest=0x5ef8 +OP_LFPGetMatchesResponse=0x1c19 +OP_LFPCommand=0x00f4 +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x1419 +OP_RaidUpdate=0x5191 +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x1fbe +OP_CombatAbility=0x11c2 +OP_SenseTraps=0x713b # Was 0x2ee0 +OP_PickPocket=0x5229 +OP_DisarmTraps=0x0000 +OP_Disarm=0x5482 +OP_Sneak=0x209e +OP_Fishing=0x3520 +OP_InstillDoubt=0x5e78 +OP_FeignDeath=0x0258 +OP_Mend=0x742c +OP_Bind_Wound=0x5f4d +OP_LDoNOpen=0x0621 + +# Task packets +OP_TaskDescription=0x3c8f +OP_TaskActivity=0x6140 +OP_CompletedTasks=0x7b7c +OP_TaskActivityComplete=0x6bb5 +OP_AcceptNewTask=0x5d0e +OP_CancelTask=0x2f6c +OP_TaskMemberList=0x16f5 # Was 0x1656 +OP_OpenNewTasksWindow=0x065c # Was 0x11de +OP_AvaliableTask=0x5ed2 # Was 0x2377 +OP_TaskHistoryRequest=0x2da2 +OP_TaskHistoryReply=0x2cc2 +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x7705 +OP_RequestTitles=0x6cde +OP_SendTitleList=0x12cc +OP_SetTitle=0x0bda +OP_SetTitleReply=0x05b3 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +OP_RAWOutOfSession=0x0000 + +# we need to document the differences between these packets to make identifying them easier +OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs +OP_InitialHPUpdate=0x0000 From e2f25d1c92ca031a8d9248036bbb67bcc70179de Mon Sep 17 00:00:00 2001 From: badcaptain Date: Thu, 25 Apr 2013 00:42:52 -0400 Subject: [PATCH 12/15] Fixed a couple of merc stat issues and a few bot aggro issues, removed unneeded bot pet AI. --- changelog.txt | 6 ++++ zone/MobAI.cpp | 9 ++++++ zone/bot.cpp | 28 ++++++++++------- zone/botspellsai.cpp | 2 +- zone/merc.cpp | 72 ++------------------------------------------ zone/merc.h | 2 +- 6 files changed, 37 insertions(+), 82 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8b7df0a4d..d39146a1e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 04/24/2013 == +Bad_Captain: Fixed a couple of merc stat issues. +Bad_Captain: Removed unneeded bot pet AI. +Bad_Captain: Fixed a few bot aggro issues. (Uleat) + == 04/20/2013 == JJ: Fixed rare case where heals from buffs could go negative. diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index a2030aa7d..348704511 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -1083,6 +1083,15 @@ void Mob::AI_Process() { RemoveFromHateList(this); return; } + +#ifdef BOTS + if (IsPet() && GetOwner()->IsBot() && target == GetOwner()) + { + // this blocks all pet attacks against owner..bot pet test (copied above check) + RemoveFromHateList(this); + return; + } +#endif //BOTS if(DivineAura()) return; diff --git a/zone/bot.cpp b/zone/bot.cpp index 51b649bce..c80981883 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3122,10 +3122,6 @@ bool Bot::Process() // Bot AI AI_Process(); - // Bot Pet AI - if(HasPet()) - PetAIProcess(); - return true; } @@ -3581,8 +3577,8 @@ void Bot::AI_Process() { if(!IsEngaged()) { if(GetFollowID()) { - if(BotOwner && BotOwner->CastToClient()->AutoAttackEnabled() && BotOwner->GetTarget() && - BotOwner->GetTarget()->IsNPC() && BotOwner->GetTarget()->GetHateAmount(BotOwner)) { + if(BotOwner && BotOwner->GetTarget() && BotOwner->GetTarget()->IsNPC() && (BotOwner->GetTarget()->GetHateAmount(BotOwner) + || BotOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(BotOwner->GetTarget())) { AddToHateList(BotOwner->GetTarget(), 1); if(HasPet()) @@ -3594,11 +3590,12 @@ void Bot::AI_Process() { if(g) { for(int counter = 0; counter < g->GroupCount(); counter++) { if(g->members[counter]) { - if(g->members[counter]->IsEngaged() && g->members[counter]->GetTarget()) { - AddToHateList(g->members[counter]->GetTarget(), 1); + Mob* tar = g->members[counter]->GetTarget(); + if(tar && tar->IsNPC() && tar->GetHateAmount(g->members[counter]) && IsAttackAllowed(g->members[counter]->GetTarget())) { + AddToHateList(tar, 1); if(HasPet()) - GetPet()->AddToHateList(g->members[counter]->GetTarget(), 1); + GetPet()->AddToHateList(tar, 1); break; } @@ -6364,6 +6361,10 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillType attack_skil SendHPUpdate(); + if(this == from) { + return; + } + // Aggro the bot's group members if(IsGrouped()) { @@ -6372,7 +6373,7 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillType attack_skil { for(int i=0; imembers[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from)) + if(g->members[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from) && g->members[i]->IsAttackAllowed(from)) { g->members[i]->AddToHateList(from, 1); } @@ -9034,7 +9035,12 @@ bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined) if(attacker && target) { - if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) + if(attacker == target) + { + hasRuleDefined = true; + Result = false; + } + else if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) { hasRuleDefined = true; Result = true; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 64937b92d..5bae0b60b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -308,7 +308,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { continue; // no buffs with illusions.. use #bot command to cast illusions - if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion)) + if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion) && tar != this) continue; //no teleport spells use #bot command to cast teleports diff --git a/zone/merc.cpp b/zone/merc.cpp index cdb63beac..ba9e5d66f 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -61,6 +61,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) skills[r] = database.GetSkillCap(GetClass(),(SkillType)r,GetLevel()); } + GetMercSize(); CalcBonuses(); SetHP(GetMaxHP()); @@ -80,7 +81,6 @@ Merc::~Merc() { void Merc::CalcBonuses() { //_ZP(Merc_CalcBonuses); - GenerateBaseStats(); memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); CalcItemBonuses(&itembonuses); @@ -117,59 +117,7 @@ void Merc::CalcBonuses() rooted = FindType(SE_Root); } -void Merc::GenerateBaseStats() { - - // base stats - uint16 Strength = _baseSTR; - uint16 Stamina = _baseSTA; - uint16 Dexterity = _baseDEX; - uint16 Agility = _baseAGI; - uint16 Wisdom = _baseWIS; - uint16 Intelligence = _baseINT; - uint16 Charisma = _baseCHA; - uint16 Attack = _baseATK; - uint16 MagicResist = _baseMR; - uint16 FireResist = _baseFR; - uint16 DiseaseResist = _baseDR; - uint16 PoisonResist = _basePR; - uint16 ColdResist = _baseCR; - uint16 CorruptionResist = _baseCorrup; - - switch(this->GetClass()) { - case 1: // Warrior - Strength += 10; - Stamina += 20; - Agility += 10; - Dexterity += 10; - Attack += 12; - break; - case 2: // Cleric - Strength += 5; - Stamina += 5; - Agility += 10; - Wisdom += 30; - Attack += 8; - break; - case 4: // Ranger - Strength += 15; - Stamina += 10; - Agility += 10; - Wisdom += 15; - Attack += 17; - break; - case 9: // Rogue - Strength += 10; - Stamina += 20; - Agility += 10; - Dexterity += 10; - Attack += 12; - break; - case 12: // Wizard - Stamina += 20; - Intelligence += 30; - Attack += 5; - break; - } +void Merc::GetMercSize() { float MercSize = GetSize(); @@ -220,20 +168,6 @@ void Merc::GenerateBaseStats() { break; } - this->_baseSTR = Strength; - this->_baseSTA = Stamina; - this->_baseDEX = Dexterity; - this->_baseAGI = Agility; - this->_baseWIS = Wisdom; - this->_baseINT = Intelligence; - this->_baseCHA = Charisma; - this->_baseATK = Attack; - this->_baseMR = MagicResist; - this->_baseFR = FireResist; - this->_baseDR = DiseaseResist; - this->_basePR = PoisonResist; - this->_baseCR = ColdResist; - this->_baseCorrup = CorruptionResist; this->size = MercSize; } @@ -940,7 +874,7 @@ int16 Merc::CalcCorrup() } int16 Merc::CalcATK() { - ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement() + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); + ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); return(ATK); } diff --git a/zone/merc.h b/zone/merc.h index dadba3fc1..4ed6e6332 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -328,7 +328,7 @@ private: int GroupLeadershipAAHealthRegeneration(); int GroupLeadershipAAOffenseEnhancement(); - void GenerateBaseStats(); + void GetMercSize(); void GenerateAppearance(); bool LoadMercSpells(); From 61f70093780d3a58349cf72a223487289f409d16 Mon Sep 17 00:00:00 2001 From: Derision Date: Thu, 25 Apr 2013 19:04:33 +0100 Subject: [PATCH 13/15] EQExtractor2 SQL gen support for current client. --- utils/EQExtractor2/EQExtractor2/ChangeLog.txt | 3 + .../EQExtractor2/EQExtractor2Form1.cs | 2 +- .../EQExtractor2/PatchApril15-2013.cs | 221 +++++++++++++++++- 3 files changed, 223 insertions(+), 3 deletions(-) diff --git a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt index abdfab715..2181b9279 100644 --- a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt +++ b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt @@ -1,5 +1,8 @@ EQExtractor2 Changelog. All changes since the 1.0 release. +==04/25/2013== +Derision: Added SQL generation support to current Live client decoder. + ==04/24/2013== Derision: Added decoder to support packet dumps from current Live client. SQL generation NOT SUPPORTED YET (need tweaks to position struct). diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs index 220c743d9..4fb5096bc 100644 --- a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs @@ -18,7 +18,7 @@ namespace EQExtractor2 { public partial class EQExtractor2Form1 : Form { - string Version = "EQExtractor2 Version 2.6.3 SVN"; + string Version = "EQExtractor2 Version 2.6.4 GIT"; static int PacketsSeen = 0; static long BytesRead = 0; diff --git a/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs index 7d8804577..9ae389832 100644 --- a/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs +++ b/utils/EQExtractor2/EQExtractor2/PatchApril15-2013.cs @@ -17,7 +17,224 @@ namespace EQExtractor2.Patches PatchConfFileName = "patch_April15-2013.conf"; - SupportsSQLGeneration = false; - } + SupportsSQLGeneration = true; + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Gender = (Bitfield & 3); + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC > 0) && ((OtherData & 1) > 0)) + { + // Destructable Objects + NewSpawn.DestructableString1 = Buffer.ReadString(false); + NewSpawn.DestructableString2 = Buffer.ReadString(false); + NewSpawn.DestructableString3 = Buffer.ReadString(false); + Buffer.SkipBytes(53); + } + + if ((OtherData & 4) > 0) + { + // Auras + Buffer.ReadString(false); + Buffer.ReadString(false); + Buffer.SkipBytes(54); + } + + NewSpawn.PropCount = Buffer.ReadByte(); + + if (NewSpawn.PropCount > 0) + NewSpawn.BodyType = Buffer.ReadUInt32(); + else + NewSpawn.BodyType = 0; + + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + + Buffer.SkipBytes(1); // Skip HP % + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.LastName = Buffer.ReadString(true); + + Buffer.SkipBytes(6); + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(25); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if ((NewSpawn.IsNPC == 0) || NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip3 = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + + Buffer.SkipBytes(20); + + NewSpawn.MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + NewSpawn.MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)((Position1 >> 12) & 0x7FFFF)); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position2) & 0x7FFFF); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 13) & 0x7FFFF); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position3 >> 13) & 0xFFF); + + if ((OtherData & 16) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 32) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + Buffer.SkipBytes(54); + + Debug.Assert(Buffer.GetPosition() == Buffer.Length(), "Length mismatch while parsing zone spawns"); + + ZoneSpawns.Add(NewSpawn); + } + return ZoneSpawns; + } } } \ No newline at end of file From 401e897019f0172366f0dd0a8da524cdf1a4bc99 Mon Sep 17 00:00:00 2001 From: Derision Date: Sat, 27 Apr 2013 15:45:24 +0100 Subject: [PATCH 14/15] Verify OP_Ack size & fix crash in BasePacket::build_raw_header_dump due to uninitialised timestamp --- changelog.txt | 3 +++ common/BasePacket.cpp | 1 + common/EQStream.cpp | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/changelog.txt b/changelog.txt index d39146a1e..731cf4cfc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/27/2013 == +Pixel Bounds: Verify OP_Ack size & fix crash in BasePacket::build_raw_header_dump due to uninitialised timestamp. + == 04/24/2013 == Bad_Captain: Fixed a couple of merc stat issues. Bad_Captain: Removed unneeded bot pet AI. diff --git a/common/BasePacket.cpp b/common/BasePacket.cpp index 2717ee354..82fd13649 100644 --- a/common/BasePacket.cpp +++ b/common/BasePacket.cpp @@ -28,6 +28,7 @@ BasePacket::BasePacket(const unsigned char *buf, uint32 len) this->size=0; this->_wpos = 0; this->_rpos = 0; + this->timestamp.tv_sec = 0; if (len>0) { this->size=len; pBuffer= new unsigned char[len]; diff --git a/common/EQStream.cpp b/common/EQStream.cpp index 561c8f4a6..7c179fab7 100644 --- a/common/EQStream.cpp +++ b/common/EQStream.cpp @@ -284,6 +284,11 @@ uint32 processed=0,subpacket_length=0; } break; case OP_Ack: { + if(!p->pBuffer || (p->Size() < 4)) + { + _log(NET__ERROR, _L "Received OP_Ack that was of malformed size" __L); + break; + } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); AckPackets(seq); From 232d1e2ca888f491f6ab1d45fc07f9c0dc906bf9 Mon Sep 17 00:00:00 2001 From: Derision Date: Sat, 27 Apr 2013 16:22:29 +0100 Subject: [PATCH 15/15] Verify minimum size of OP_Packet, OP_Fragment and OP_OutOfOrderAck --- changelog.txt | 1 + common/EQStream.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/changelog.txt b/changelog.txt index 731cf4cfc..fc815952e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 04/27/2013 == Pixel Bounds: Verify OP_Ack size & fix crash in BasePacket::build_raw_header_dump due to uninitialised timestamp. +Derision: Verify minimum size of OP_Packet, OP_Fragment and OP_OutOfOrderAck. == 04/24/2013 == Bad_Captain: Fixed a couple of merc stat issues. diff --git a/common/EQStream.cpp b/common/EQStream.cpp index 7c179fab7..ae5c6953c 100644 --- a/common/EQStream.cpp +++ b/common/EQStream.cpp @@ -173,6 +173,11 @@ uint32 processed=0,subpacket_length=0; break; case OP_Packet: { + if(!p->pBuffer || (p->Size() < 4)) + { + _log(NET__ERROR, _L "Received OP_Packet that was of malformed size" __L); + break; + } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { @@ -218,6 +223,11 @@ uint32 processed=0,subpacket_length=0; break; case OP_Fragment: { + if(!p->pBuffer || (p->Size() < 4)) + { + _log(NET__ERROR, _L "Received OP_Fragment that was of malformed size" __L); + break; + } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { @@ -388,6 +398,11 @@ uint32 processed=0,subpacket_length=0; } break; case OP_OutOfOrderAck: { + if(!p->pBuffer || (p->Size() < 4)) + { + _log(NET__ERROR, _L "Received OP_OutOfOrderAck that was of malformed size" __L); + break; + } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); MOutboundQueue.lock();