Fix exploit with expendable AAs

This commit is contained in:
Michael Cook (mackal) 2015-06-08 02:00:44 -04:00
parent db307d865b
commit 6229b90451
4 changed files with 233 additions and 215 deletions

View File

@ -3,6 +3,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50)
== 06/07/2015 == == 06/07/2015 ==
Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed. Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed.
Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql
mackal: changes to AA packets since more fields have been identified
mackal: fix exploit with expendable AAs punching holes in the aa_array and staying around longer than they are welcomed
== 05/25/2015 == == 05/25/2015 ==
Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods

View File

@ -1104,7 +1104,7 @@ void Client::SendAATable() {
uint32 i; uint32 i;
for(i=0;i < MAX_PP_AA_ARRAY;i++){ for(i=0;i < MAX_PP_AA_ARRAY;i++){
aa2->aa_list[i].AA = aa[i]->AA; aa2->aa_list[i].AA = aa[i]->value ? aa[i]->AA : 0; // bit of a hack to prevent expendables punching a hole
aa2->aa_list[i].value = aa[i]->value; aa2->aa_list[i].value = aa[i]->value;
aa2->aa_list[i].charges = aa[i]->charges; aa2->aa_list[i].charges = aa[i]->charges;
} }
@ -1402,8 +1402,6 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) {
aa[cur]->value = new_value; aa[cur]->value = new_value;
if(new_value > 0) if(new_value > 0)
aa[cur]->AA++; aa[cur]->AA++;
else
aa[cur]->AA = 0;
aa[cur]->charges = charges; aa[cur]->charges = charges;
return true; return true;
} }
@ -1411,8 +1409,12 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) {
aa[cur]->value = new_value; aa[cur]->value = new_value;
if(new_value > 0) if(new_value > 0)
aa[cur]->AA++; aa[cur]->AA++;
else aa[cur]->charges = charges;
aa[cur]->AA = 0; return true;
}
// hack to prevent expendable exploit, we should probably be reshuffling the array to fix the hole
else if(aa[cur]->value == 0 && new_value == 1 && aa[cur]->AA == aa_id) {
aa[cur]->value = new_value;
aa[cur]->charges = charges; aa[cur]->charges = charges;
return true; return true;
} }

View File

@ -549,17 +549,22 @@ bool Client::SaveAA(){
} }
} }
m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; m_pp.aapoints_spent = spentpoints + m_epp.expended_aa;
int highest = 0;
for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { for (int a = 0; a < MAX_PP_AA_ARRAY; a++) {
if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load
if (first_entry != 1){ if (first_entry != 1){
rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)" rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); " VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
first_entry = 1; first_entry = 1;
} else {
rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
} }
rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); highest = a;
} }
} }
auto results = database.QueryDatabase(rquery); auto results = database.QueryDatabase(rquery);
/* This is another part of the hack to clean up holes left by expendable AAs */
rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` >= %d", character_id, highest);
return true; return true;
} }

View File

@ -1452,11 +1452,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
"`character_alternate_abilities` " "`character_alternate_abilities` "
"WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); "WHERE `id` = %u ORDER BY `slot`", this->CharacterID());
results = database.QueryDatabase(query); i = 0; results = database.QueryDatabase(query); i = 0;
int offset = 0; // offset to fix the hole from expendables
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]); i = atoi(row[0]) - offset;
m_pp.aa_array[i].AA = atoi(row[1]); m_pp.aa_array[i].AA = atoi(row[1]);
m_pp.aa_array[i].value = atoi(row[2]); m_pp.aa_array[i].value = atoi(row[2]);
m_pp.aa_array[i].charges = atoi(row[3]); m_pp.aa_array[i].charges = atoi(row[3]);
/* A used expendable could cause there to be a "hole" in the array, this is very bad. Bad things like keeping your expendable after use.
We could do a few things, one of them being reshuffling when the hole is created or defer the fixing until a later point, like during load!
Or just never making a hole in the array and just have hacks every where. Fixing the hole at load really just keeps 1 hack in Client::SendAATable
and keeping this offset that will cause the next AA to be pushed back over the hole. We also need to clean up on save so we don't have multiple
entries for a single AA.
*/
if (m_pp.aa_array[i].value == 0)
offset++;
} }
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){
uint32 id = aa[a]->AA; uint32 id = aa[a]->AA;